From 454cd2d342f2b00133bbad49f8398d8c7f05f73b Mon Sep 17 00:00:00 2001 From: Joel Thomas Date: Tue, 14 Apr 2026 14:58:33 +0530 Subject: [PATCH] Implement Payslip Generation Feature EMP009 : Generate Payslips - Updated Payslip model to store employee, salary, and date details - Added payslip storage to DataStore and file load/save support - Added logic to generate payslips once per month per employee - Restricted payslip generation to finance role - Linked payslips to employees during load and creation - Added month, year, and day helpers to Timestamp - Added payslip generation option to Finance Executive menu Smitha Mohan --- .../controllers/ZenvyController.cpp | 12 ++- .../controllers/ZenvyController.h | 1 + .../Trenser.Zenvy/datastores/DataStore.cpp | 5 ++ .../Trenser.Zenvy/datastores/DataStore.h | 4 + .../Trenser.Zenvy/models/Employee.cpp | 2 +- Trenser.Zenvy/Trenser.Zenvy/models/Payroll.h | 2 +- .../Trenser.Zenvy/models/Payslip.cpp | 75 ++++++++++++++++++- Trenser.Zenvy/Trenser.Zenvy/models/Payslip.h | 19 ++++- .../services/ApplicationConfig.h | 1 + .../services/PayslipManagementService.cpp | 65 +++++++++++++++- .../services/PayslipManagementService.h | 3 + .../Trenser.Zenvy/utilities/Timestamp.cpp | 21 ++++++ .../Trenser.Zenvy/utilities/Timestamp.h | 3 + .../views/FinanceExecutiveMenu.cpp | 15 +++- .../views/FinanceExecutiveMenu.h | 1 + 15 files changed, 218 insertions(+), 11 deletions(-) diff --git a/Trenser.Zenvy/Trenser.Zenvy/controllers/ZenvyController.cpp b/Trenser.Zenvy/Trenser.Zenvy/controllers/ZenvyController.cpp index da75688..aced692 100644 --- a/Trenser.Zenvy/Trenser.Zenvy/controllers/ZenvyController.cpp +++ b/Trenser.Zenvy/Trenser.Zenvy/controllers/ZenvyController.cpp @@ -37,24 +37,32 @@ std::pair>> Zen return m_employeeManagementService->searchEmployee(name); } +std::shared_ptr ZenvyController::getCurrentEmployee() +{ + return m_employeeManagementService->getCurrentEmployee(); +} + +//Payslip Management void ZenvyController::updateSalary(const std::string& employeeId, double basicSalary, double houseRentAllowance, double foodAllowance, double employeePFContribution, double employerPFContribution) { m_payslipManagementService->updateSalary(employeeId, basicSalary, houseRentAllowance, foodAllowance, employeePFContribution, employerPFContribution); } -std::shared_ptr ZenvyController::getCurrentEmployee() +void ZenvyController::generatePayslips() { - return m_employeeManagementService->getCurrentEmployee(); + m_payslipManagementService->generatePayslips(); } void ZenvyController::loadStates() { m_employeeManagementService->loadEmployees(); m_payslipManagementService->loadPayrolls(); + m_payslipManagementService->loadPayslips(); } void ZenvyController::persistStates() { m_employeeManagementService->saveEmployees(); m_payslipManagementService->savePayrolls(); + m_payslipManagementService->savePayslips(); } diff --git a/Trenser.Zenvy/Trenser.Zenvy/controllers/ZenvyController.h b/Trenser.Zenvy/Trenser.Zenvy/controllers/ZenvyController.h index c2bcc84..56140de 100644 --- a/Trenser.Zenvy/Trenser.Zenvy/controllers/ZenvyController.h +++ b/Trenser.Zenvy/Trenser.Zenvy/controllers/ZenvyController.h @@ -59,6 +59,7 @@ public: //Payslip management void updateSalary(const std::string&, double, double, double, double, double); + void generatePayslips(); //File Management void loadStates(); diff --git a/Trenser.Zenvy/Trenser.Zenvy/datastores/DataStore.cpp b/Trenser.Zenvy/Trenser.Zenvy/datastores/DataStore.cpp index dadcfdc..fe9dad4 100644 --- a/Trenser.Zenvy/Trenser.Zenvy/datastores/DataStore.cpp +++ b/Trenser.Zenvy/Trenser.Zenvy/datastores/DataStore.cpp @@ -31,3 +31,8 @@ payrollMap& DataStore::getPayrolls() { return m_payrolls; } + +payslipMap& DataStore::getPayslips() +{ + return m_payslips; +} diff --git a/Trenser.Zenvy/Trenser.Zenvy/datastores/DataStore.h b/Trenser.Zenvy/Trenser.Zenvy/datastores/DataStore.h index d05d5f4..9f79fc8 100644 --- a/Trenser.Zenvy/Trenser.Zenvy/datastores/DataStore.h +++ b/Trenser.Zenvy/Trenser.Zenvy/datastores/DataStore.h @@ -19,9 +19,11 @@ #include "Announcement.h" #include "Faq.h" #include "Payroll.h" +#include "Payslip.h" using employeeMap = std::map>; using payrollMap = std::map>; +using payslipMap = std::map>; using logMap = std::map>; class DataStore @@ -30,6 +32,7 @@ private: std::shared_ptr m_authenticatedEmployee; employeeMap m_employees; payrollMap m_payrolls; + payslipMap m_payslips; logMap m_logs; DataStore() = default; public: @@ -40,6 +43,7 @@ public: DataStore& operator=(DataStore&&) = delete; employeeMap& getEmployees(); payrollMap& getPayrolls(); + payslipMap& getPayslips(); logMap& getLogs(); std::shared_ptr& getAuthenticatedEmployee(); void setAuthenticatedEmployee(std::shared_ptr < Employee>); diff --git a/Trenser.Zenvy/Trenser.Zenvy/models/Employee.cpp b/Trenser.Zenvy/Trenser.Zenvy/models/Employee.cpp index 8b5aa80..9f7adf9 100644 --- a/Trenser.Zenvy/Trenser.Zenvy/models/Employee.cpp +++ b/Trenser.Zenvy/Trenser.Zenvy/models/Employee.cpp @@ -143,7 +143,7 @@ void Employee::addPayslip(std::shared_ptr payslip) { if (payslip) { - m_payslips[payslip->getPayslipId()] = payslip; + m_payslips[payslip->getId()] = payslip; } } diff --git a/Trenser.Zenvy/Trenser.Zenvy/models/Payroll.h b/Trenser.Zenvy/Trenser.Zenvy/models/Payroll.h index 6b063c8..cd747ed 100644 --- a/Trenser.Zenvy/Trenser.Zenvy/models/Payroll.h +++ b/Trenser.Zenvy/Trenser.Zenvy/models/Payroll.h @@ -52,7 +52,7 @@ public: void setFoodAllowance(double); void setEmployeePFContribution(double); void setEmployerPFContribution(double); - virtual std::string serialize() const; + std::string serialize() const; static std::shared_ptr deserialize(const std::string&); static std::string getHeaders(); }; \ No newline at end of file diff --git a/Trenser.Zenvy/Trenser.Zenvy/models/Payslip.cpp b/Trenser.Zenvy/Trenser.Zenvy/models/Payslip.cpp index 614e525..91e0b38 100644 --- a/Trenser.Zenvy/Trenser.Zenvy/models/Payslip.cpp +++ b/Trenser.Zenvy/Trenser.Zenvy/models/Payslip.cpp @@ -1,8 +1,27 @@ +#include #include "Payslip.h" +#include "StringHelper.h" +#include "Factory.h" int Payslip::m_uid = 0; -const std::string& Payslip::getPayslipId() const +Payslip::Payslip(const std::string& id, + const std::string& employeeId, + double salary, + util::Timestamp timestamp) + : m_id(id), + m_employeeId(employeeId), + m_salary(salary), + m_timestamp(timestamp) +{ + int idNumber = util::extractNumber(m_id); + if (idNumber > m_uid) + { + m_uid = idNumber; + } +} + +const std::string& Payslip::getId() const { return m_id; } @@ -20,4 +39,56 @@ void Payslip::setPayslipId(const std::string& id) void Payslip::setSalary(double salary) { m_salary = salary; -} \ No newline at end of file +} + +const util::Timestamp& Payslip::getTimestamp() const +{ + return m_timestamp; +} + +const std::string& Payslip::getEmployeeId() const +{ + return m_employeeId; +} + +std::string Payslip::serialize() const +{ + std::ostringstream serializedPayslip; + serializedPayslip << m_id << ',' + << m_employeeId << ',' + << m_salary << ',' + << m_timestamp.toString(); + return serializedPayslip.str(); +} + +std::shared_ptr Payslip::deserialize(const std::string& record) +{ + std::string id, employeeId, timestampString; + std::string salaryString; + std::istringstream serializedPayslip(record); + std::getline(serializedPayslip, id, ','); + std::getline(serializedPayslip, employeeId, ','); + std::getline(serializedPayslip, salaryString, ','); + std::getline(serializedPayslip, timestampString, ','); + + try + { + double salary = std::stod(salaryString); + util::Timestamp timestamp = util::Timestamp::fromString(timestampString); + return Factory::getObject( + id, + employeeId, + salary, + timestamp + ); + } + catch (...) + { + throw std::runtime_error("Failed to deserialize Payslip object"); + } +} + +std::string Payslip::getHeaders() +{ + return "PayslipId,EmployeeId,Salary,Timestamp"; +} diff --git a/Trenser.Zenvy/Trenser.Zenvy/models/Payslip.h b/Trenser.Zenvy/Trenser.Zenvy/models/Payslip.h index 10dc3bf..b8b971d 100644 --- a/Trenser.Zenvy/Trenser.Zenvy/models/Payslip.h +++ b/Trenser.Zenvy/Trenser.Zenvy/models/Payslip.h @@ -1,17 +1,30 @@ #pragma once +#include #include +#include "Timestamp.h" class Payslip { private: static int m_uid; std::string m_id; + std::string m_employeeId; double m_salary; + util::Timestamp m_timestamp; public: - Payslip() : m_id("PS" + std::to_string(++m_uid)), m_salary(0.0) {} - Payslip(const double salary) : m_id("PS" + std::to_string(++m_uid)), m_salary(salary) {} - const std::string& getPayslipId() const; + Payslip() : m_id("PS" + std::to_string(++m_uid)), m_employeeId(""), m_salary(0.0) {} + Payslip(const double salary, const std::string& employeeId) : m_id("PS" + std::to_string(++m_uid)), m_employeeId(employeeId), m_salary(salary) {} + Payslip(const std::string& id, + const std::string& employeeId, + double salary, + util::Timestamp timestamp); + const std::string& getId() const; double getSalary() const; void setPayslipId(const std::string& id); void setSalary(double salary); + const util::Timestamp& getTimestamp() const; + const std::string& getEmployeeId() const; + std::string serialize() const; + static std::shared_ptr deserialize(const std::string&); + static std::string getHeaders(); }; \ No newline at end of file diff --git a/Trenser.Zenvy/Trenser.Zenvy/services/ApplicationConfig.h b/Trenser.Zenvy/Trenser.Zenvy/services/ApplicationConfig.h index 25231c2..0fd3b49 100644 --- a/Trenser.Zenvy/Trenser.Zenvy/services/ApplicationConfig.h +++ b/Trenser.Zenvy/Trenser.Zenvy/services/ApplicationConfig.h @@ -36,5 +36,6 @@ namespace Config constexpr const char* EMPLOYEES_FILE = "files/Employee.csv"; constexpr const char* GENERAL_EMPLOYEES_FILE = "files/GeneralEmployee.csv"; constexpr const char* PAYROLL_FILE = "files/Payroll.csv"; + constexpr const char* PAYSLIP_FILE = "files/Payslip.csv"; } } diff --git a/Trenser.Zenvy/Trenser.Zenvy/services/PayslipManagementService.cpp b/Trenser.Zenvy/Trenser.Zenvy/services/PayslipManagementService.cpp index ecc326d..0d43120 100644 --- a/Trenser.Zenvy/Trenser.Zenvy/services/PayslipManagementService.cpp +++ b/Trenser.Zenvy/Trenser.Zenvy/services/PayslipManagementService.cpp @@ -1,9 +1,11 @@ #include +#include #include "PayslipManagementService.h" #include "ApplicationConfig.h" #include "AuthorizationHelper.h" #include "Enums.h" #include "FileManager.h" +#include "Factory.h" void PayslipManagementService::updateSalary(const std::string& employeeId, double basicSalary, double houseRentAllowance, double foodAllowance, double employeePFContribution, double employerPFContribution) { @@ -24,6 +26,42 @@ void PayslipManagementService::updateSalary(const std::string& employeeId, doubl } } +void PayslipManagementService::generatePayslips() +{ + util::enforceAuthorization(m_dataStore.getAuthenticatedEmployee()->getEmployeeType(), Enums::EmployeeType::FINANCE); + auto& employees = m_dataStore.getEmployees(); + auto& payslips = m_dataStore.getPayslips(); + util::Timestamp currentTimestamp; + int currentMonth = currentTimestamp.getMonth(); + int currentYear = currentTimestamp.getYear(); + for (const auto& employeePair : employees) + { + auto& employee = employeePair.second; + if (employee->getEmployeeType() == Enums::EmployeeType::ADMIN) + { + continue; + } + auto& employeePayslips = employee->getEmployeePayslips(); + auto payslipForTheMonth = std::find_if(employeePayslips.begin(), employeePayslips.end(), + [currentMonth, currentYear](const auto& payslipPair) + { + auto& payslip = payslipPair.second; + auto& timestamp = payslip->getTimestamp(); + return (timestamp.getMonth() == currentMonth && timestamp.getYear() == currentYear); + } + ); + if (payslipForTheMonth == employeePayslips.end()) + { + auto payroll = employee->getPayroll(); + double salary; + salary = payroll->getBasicSalary() + payroll->getFoodAllowance() + payroll->getHouseRentAllowance() - payroll->getEmployeePFContribution() - payroll->getEmployerPFContribution(); + std::shared_ptr payslip = Factory::getObject(salary, employee->getId()); + employee->addPayslip(payslip); + payslips.emplace(payslip->getId(), payslip); + } + } +} + void PayslipManagementService::loadPayrolls() { FileManager payrollFileManager(Config::File::PAYROLL_FILE); @@ -47,4 +85,29 @@ void PayslipManagementService::savePayrolls() FileManager payrollFileManager(Config::File::PAYROLL_FILE); auto& payrolls = m_dataStore.getPayrolls(); payrollFileManager.save(payrolls); -} \ No newline at end of file +} + +void PayslipManagementService::loadPayslips() +{ + FileManager payslipFileManager(Config::File::PAYSLIP_FILE); + auto& payslips = m_dataStore.getPayslips(); + auto& employees = m_dataStore.getEmployees(); + auto payslipObjects = payslipFileManager.load(); + for (const auto& payslipPair : payslipObjects) + { + auto employeeIterator = employees.find(payslipPair.second->getEmployeeId()); + if (employeeIterator == employees.end()) + { + throw std::runtime_error("Payslip Object not associated with an existing employee"); + } + employeeIterator->second->addPayslip(payslipPair.second); + } + payslips.insert(payslipObjects.begin(), payslipObjects.end()); +} + +void PayslipManagementService::savePayslips() +{ + FileManager payslipFileManager(Config::File::PAYSLIP_FILE); + auto& payslips = m_dataStore.getPayslips(); + payslipFileManager.save(payslips); +} diff --git a/Trenser.Zenvy/Trenser.Zenvy/services/PayslipManagementService.h b/Trenser.Zenvy/Trenser.Zenvy/services/PayslipManagementService.h index fd91da1..298c248 100644 --- a/Trenser.Zenvy/Trenser.Zenvy/services/PayslipManagementService.h +++ b/Trenser.Zenvy/Trenser.Zenvy/services/PayslipManagementService.h @@ -10,6 +10,9 @@ private: public: PayslipManagementService() : m_dataStore(DataStore::getInstance()) {}; void updateSalary(const std::string&, double, double, double, double, double); + void generatePayslips(); void loadPayrolls(); void savePayrolls(); + void loadPayslips(); + void savePayslips(); }; diff --git a/Trenser.Zenvy/Trenser.Zenvy/utilities/Timestamp.cpp b/Trenser.Zenvy/Trenser.Zenvy/utilities/Timestamp.cpp index 42d4758..1341da9 100644 --- a/Trenser.Zenvy/Trenser.Zenvy/utilities/Timestamp.cpp +++ b/Trenser.Zenvy/Trenser.Zenvy/utilities/Timestamp.cpp @@ -50,6 +50,27 @@ int util::Timestamp::getDateAsInt() const return year * 10000 + month * 100 + day; } +int util::Timestamp::getMonth() const +{ + std::tm timeStruct{}; + localtime_s(&timeStruct, &m_time); + return timeStruct.tm_mon + 1; +} + +int util::Timestamp::getYear() const +{ + std::tm timeStruct{}; + localtime_s(&timeStruct, &m_time); + return timeStruct.tm_year + 1900; +} + +int util::Timestamp::getDay() const +{ + std::tm timeStruct{}; + localtime_s(&timeStruct, &m_time); + return timeStruct.tm_mday; +} + double util::Timestamp::getDurationInMinutes(const Timestamp& startTimestamp, const Timestamp& endTimestamp) { return getDurationInSeconds(startTimestamp, endTimestamp) / 60.0; diff --git a/Trenser.Zenvy/Trenser.Zenvy/utilities/Timestamp.h b/Trenser.Zenvy/Trenser.Zenvy/utilities/Timestamp.h index abbc58f..8aedc35 100644 --- a/Trenser.Zenvy/Trenser.Zenvy/utilities/Timestamp.h +++ b/Trenser.Zenvy/Trenser.Zenvy/utilities/Timestamp.h @@ -17,6 +17,9 @@ namespace util static double getDurationInMinutes(const Timestamp&, const Timestamp&); static double getDurationInSeconds(const Timestamp&, const Timestamp&); int getDateAsInt() const; + int getMonth() const; + int getYear() const; + int getDay() const; bool operator>(const Timestamp&) const; bool operator<(const Timestamp&) const; bool operator>=(const Timestamp&) const; diff --git a/Trenser.Zenvy/Trenser.Zenvy/views/FinanceExecutiveMenu.cpp b/Trenser.Zenvy/Trenser.Zenvy/views/FinanceExecutiveMenu.cpp index 70885aa..74aadbc 100644 --- a/Trenser.Zenvy/Trenser.Zenvy/views/FinanceExecutiveMenu.cpp +++ b/Trenser.Zenvy/Trenser.Zenvy/views/FinanceExecutiveMenu.cpp @@ -3,6 +3,7 @@ #include "InputHelper.h" #include "OutputHelper.h" #include "MenuHelper.h" +#include "Timestamp.h" void FinanceExecutiveMenu::run() { @@ -13,7 +14,7 @@ void FinanceExecutiveMenu::run() { int choice; util::clear(); - std::cout << "Finance Executive Menu\n1. Apply Leave\n2. View Payslip\n3. View Payslip History\n4. View Employees\n5. Search Employee\n6. View Notification\n7. View Announcements\n8. Resolve Ticket\n9. Generate Payslip\n10. Update Payroll\n11. Update Profile\n12. View Profile \n13. Logout\nEnter your Choice: "; + std::cout << "Finance Executive Menu\n1. Apply Leave\n2. View Payslip\n3. View Payslip History\n4. View Employees\n5. Search Employee\n6. View Notification\n7. View Announcements\n8. Resolve Ticket\n9. Generate Payslips\n10. Update Payroll\n11. Update Profile\n12. View Profile \n13. Logout\nEnter your Choice: "; util::read(choice); if (!handleOperation(choice)) { @@ -51,6 +52,15 @@ void FinanceExecutiveMenu::updatePayroll() } } +void FinanceExecutiveMenu::generatePayslips() +{ + util::Timestamp currentTimestamp; + util::clear(); + m_zenvyController->generatePayslips(); + std::cout << "Generated payslips for the month of " << currentTimestamp.getMonth() << "-" << currentTimestamp.getYear() << " successfully."; + util::pressEnter(); +} + bool FinanceExecutiveMenu::handleOperation(int choice) { switch (choice) @@ -61,6 +71,9 @@ bool FinanceExecutiveMenu::handleOperation(int choice) case 5: searchEmployee(m_zenvyController); break; + case 9: + generatePayslips(); + break; case 10: updatePayroll(); break; diff --git a/Trenser.Zenvy/Trenser.Zenvy/views/FinanceExecutiveMenu.h b/Trenser.Zenvy/Trenser.Zenvy/views/FinanceExecutiveMenu.h index a5a2016..d757a03 100644 --- a/Trenser.Zenvy/Trenser.Zenvy/views/FinanceExecutiveMenu.h +++ b/Trenser.Zenvy/Trenser.Zenvy/views/FinanceExecutiveMenu.h @@ -14,5 +14,6 @@ public: void run(); bool handleOperation(int); void updatePayroll(); + void generatePayslips(); };