diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..4fa52e0 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,57 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Overview + +CEL Payroll is a .NET Framework 4.5 WinForms desktop payroll/HR application (C#) backed by SQL Server. Solution: `CEL Payroll.sln` (VS 2019 format; folder name refers to the original VS2015 port). There are no test projects and no linter. + +## Build + +Build from Visual Studio, or with MSBuild from a Developer Command Prompt: + +```bash +msbuild "CEL Payroll.sln" /p:Configuration=Debug +``` + +Startup project is `Payroll.UI` (WinExe, entry `Payroll.UI/Payroll.UI/Program.cs` → `frmMain`). Third-party and in-house framework assemblies (`Ease.CoreV35`, `Ease.Controls`, `Ease.Report`, ReportViewer, NPOI, ClosedXML, iTextSharp, etc.) are checked in as binaries under `Library/` and referenced by HintPath, not NuGet. Crystal Reports 10.5 runtime must be installed for `Payroll.Report` to build. + +## Architecture + +Layered, with project dependencies flowing downward: + +- **Payroll.UI** — WinForms screens, grouped by module folder (Salary, Tax, Bonus, Leave, PF, Gratuity, FinalSettlement, …). +- **Payroll.Report** — report classes (`Class/rpt*.cs`), the report viewer (`UI/fReportViewer.cs`), typed datasets (`PayrollDataSet`), and `.rdlc` files under `RDLC/` (plus some Crystal reports). +- **Ease.UICreator** — generic, config-driven master/detail form generator used for simple setup screens. +- **Payroll.Controls** — shared custom WinForms controls. +- **Payroll.Service** — service implementations + data access (SQL). +- **Payroll.BO** — business objects and the service interfaces. +- **Ease.SvcHost** — Windows service exposing `Payroll.Service` over .NET Remoting (TCP) for a 3-tier deployment. +- **Payroll.NotifProcessInterface** — console exe that runs notification/mail processing. + +### BO / Service / DA pattern + +Each entity spans three files that share a module folder name across projects, e.g. Bonus: + +1. `Payroll.BO/Bonus/Bonus.cs` — class deriving from `BasicBaseObject` (or similar Ease.CoreV35 base), properties call `OnPropertyChange`, static `Get(...)`/instance `Save()`/`Delete()` methods that use a per-type `Cache` and delegate to `Service`. The **service interface (`IBonusService`) is declared at the bottom of the same BO file**. `Service` is resolved via `Services.Factory.CreateService(...)`. +2. `Payroll.Service/Bonus/Service/BonusService.cs` — `ServiceTemplate` subclass implementing the interface. Every method wraps work in `TransactionContext.Begin()` / `tc.End()`, with `tc.HandleError()` + `ExceptionLog.Write(e)` + `throw new ServiceException(...)` in the catch. `MapObject` maps `DataReader` columns to BO properties and sets `ObjectState.Saved`. +3. `Payroll.Service/Bonus/DA/BonusDA.cs` — `internal static` class of raw SQL. Queries use Ease's placeholder formatting, not SqlParameters: `%n` number, `%s` string, `%d` date (e.g. `tc.ExecuteNonQuery("UPDATE Bonus SET code=%s WHERE BonusID=%n", ...)`). + +Adding a method to an entity means touching all three: interface + BO wrapper, service implementation, DA query. IDs use the Ease `ID` type (`.Integer` for the int value). Many queries are scoped by `SystemInformation.CurrentSysInfo.PayrollTypeID`. + +Service location is controlled by `serviceHandler` in `Payroll.UI/Payroll.UI/App.config`: `Payroll.Service.dll/conn` loads the services in-process using the `conn` entry in ``; a `host:port/Payroll.Service` value goes through `Ease.SvcHost` remoting instead. + +### Menus and forms + +The main menu is driven by `Payroll.UI/Payroll.UI/Menu.xml`. Each `` names a `` and ``; `frmMain` resolves the form type by reflection (`Payroll.UI.` unless the name is assembly-qualified) and invokes the parameterless show method. Simple setup screens use `BasicUIForms` (`CreateUI.cs`), whose `show*` methods build forms through `Ease.UICreator`. Adding a screen to the menu means adding a Menu.xml entry whose names match exactly. + +### Reports + +RDLC files are compiled as **EmbeddedResource** in `Payroll.Report.csproj` and loaded by manifest name, e.g. `"Payroll.Report.RDLC.BankAdvice.rdlc"` (see `Class/rptBankAdvice.cs`, `UI/fReportViewer.cs`). A new `.rdlc` must be added to the csproj as an EmbeddedResource, and its dataset fields must match the `DataTable` passed in (typed datasets live in `PayrollDataSet`). + +## Repository conventions + +- Branching: work on `dev_shahadat`, PRs into `devqc` (Gitea-hosted). +- Commit messages are prefixed with the ticket ID in the form `YYYYMMDD-NNNN `, e.g. `20260929-3216 Bank advice Salary Report Modification`. +- Release commits typically bump `AssemblyVersion`/`AssemblyFileVersion` in `Payroll.UI/Payroll.UI/Properties/AssemblyInfo.cs` to a date-based `YY.M.DD.0` value. +- `App.config` contains live DB connection strings and is frequently changed per environment; don't commit connection-string changes unless intended. diff --git a/Payroll.BO/Salary/SalaryCalculator.cs b/Payroll.BO/Salary/SalaryCalculator.cs index a8a908c..0df9b8a 100644 --- a/Payroll.BO/Salary/SalaryCalculator.cs +++ b/Payroll.BO/Salary/SalaryCalculator.cs @@ -1512,41 +1512,6 @@ namespace Payroll.BO if (Param.Details.Count > 0) this.AddDetail(salary, EnumSalaryGroup.Miscellaneous, EnumSalaryItemCode.Tot_UnauthLeave_Days, Param.UnAhuthorizeLeaveID.Integer, 0, "UnAuthorized Leave Days", item.BasicCurrentDeduct); - #region Deduct all allowances for unauthorized leave - // Same days and daily-rate formula as Basic; allowances already set in the parameter are skipped to avoid double deduction - if (item.BasicCurrentDeduct > 0) - { - List allowances = salary.Details.FindAll(delegate(SalaryMonthlyDetail dtl) - { - return dtl.itemGroupCode == EnumSalaryGroup.Gross && dtl.ItemCode == EnumSalaryItemCode.Allowance; - }); - - foreach (SalaryMonthlyDetail allowance in allowances) - { - bool isInParam = Param.Details.Exists(delegate(UnAuthorizeLeaveParamDetail pd) - { - return pd.Type == EnumSalaryComponent.Allowance && pd.AllowanceID.Integer == allowance.ItemID; - }); - if (isInParam) continue; - - nAmount = allowance.CalculatedAmount; - if (salary.Employee.JoiningDate > GlobalFunctions.FirstDateOfMonth(_processMonth)) - { - double ndays = Ease.CoreV35.Utility.Global.DateFunctions.DateDiff("d", salary.Employee.JoiningDate, _processMonth) + 1; - nAmount = nAmount / ndays; - nAmount = nAmount * _processMonth.Day; - } - - nAmount = GlobalFunctions.ConevrtToDlyRateofMonth(nAmount, _processMonth) * item.BasicCurrentDeduct; - nAmount = GlobalFunctions.Round(nAmount); - if (nAmount <= 0) continue; - - this.AddDetail(salary, EnumSalaryGroup.UnauthLeave, EnumSalaryItemCode.Allowance, - allowance.ItemID, allowance.SupportID.Integer, allowance.Description, nAmount); - } - } - #endregion - //} //item.currentDeduct = nUnpaidDays;