update core calc
-unknown
golang
3 months ago
55 kB
8
Indexable
package usecase
import (
"context"
"database/sql"
"errors"
"fmt"
"sf-payroll-th/pkg/consts"
"sf-payroll-th/pkg/helper"
"sf-payroll-th/pkg/util"
"time"
"github.com/doug-martin/goqu/v9"
"github.com/doug-martin/goqu/v9/exp"
"github.com/jmoiron/sqlx"
"github.com/spf13/cast"
componentstdconst "gitlab.dataon.com/gophers/sf-payroll/modules/core/component/constant"
paymentprocessstd "gitlab.dataon.com/gophers/sf-payroll/modules/core/payment"
"gitlab.dataon.com/gophers/sf-payroll/modules/core/payment/entity"
paymententitystd "gitlab.dataon.com/gophers/sf-payroll/modules/core/payment/entity"
periodentitystd "gitlab.dataon.com/gophers/sf-payroll/modules/core/period/entity"
salaryparamconst "gitlab.dataon.com/gophers/sf-payroll/modules/core/salary-param/constant"
employeentitystd "gitlab.dataon.com/gophers/sf-payroll/modules/general/employee/entity"
helperstd "gitlab.dataon.com/gophers/sf-payroll/pkg/helper"
"gitlab.dataon.com/gophers/sf7-lib/v2/log"
"gitlab.dataon.com/gophers/sf7-sdk/database"
"gitlab.dataon.com/gophers/sf7-sdk/rbac"
"gitlab.dataon.com/gophers/sf7-sdk/sfquery"
)
type coreCalculationLogic struct {
companyID int
currentTime time.Time
employeeData employeentitystd.EmployeePersonalCompanySalary
req paymentprocessstd.PaymentProcessRequest
period periodentitystd.MasterPayrollPeriod
procYtdhID string
procMtdhID string
userLoginData rbac.AuthUser
vp *ThPayment
Salary float64
SalaryTax float64
SalaryPeriod float64
RateTax float64
RatePeriod float64
}
func (vp *ThPayment) CoreCalculation(ctx context.Context, db *sqlx.DB, req paymentprocessstd.PaymentProcessRequest) (err error) {
wr := log.FromCtx(ctx)
wr.Dbg("Custom Core Calculation")
var l coreCalculationLogic
l.companyID = cast.ToInt(ctx.Value(consts.CompanyId))
l.currentTime = time.Now().In(util.LoadTimeLocation())
l.userLoginData = util.GetFromContext[rbac.AuthUser](ctx, consts.AuthUser)
l.req = req
l.vp = vp
db, err = database.ClientConnectionFromCtx(ctx, "PAYROLL")
if err != nil {
wr.Err(consts.LogMessageDBFailedToConnect, log.Error(err), log.StackTrace())
return err
}
tx, err := db.BeginTxx(ctx, &sql.TxOptions{Isolation: sql.LevelReadUncommitted})
if err != nil {
return fmt.Errorf("failed to begin transaction for CoreCalculation: %w", err)
}
defer func() {
helper.CommitOrRollback(ctx, tx, err)
}()
period, _ := vp.Core.Period.GetPeriod(ctx, tx, helperstd.ParamRepository{
Columns: []any{
goqu.I("period_code"),
goqu.I("period_type"),
goqu.I("paydate"),
goqu.I("taxdate"),
goqu.I("usesalary"),
goqu.I("salarystartdate"),
goqu.I("salaryenddate"),
goqu.I("currency_code"),
},
Conditions: goqu.And(
goqu.I("period_code").Eq(req.PeriodCode),
goqu.I("company_id").Eq(l.companyID),
),
})
l.period = period
l.employeeData, _ = l.vp.Core.Employee.GetEmployeePersonalCompanySalary(ctx, tx, helperstd.ParamRepository{
Columns: []any{
// salary param
goqu.T(salaryparamconst.TableNameTPYDEmpSalaryParam).Col("currency_code"),
goqu.T(salaryparamconst.TableNameTPYDEmpSalaryParam).Col("effective_date"),
goqu.T(salaryparamconst.TableNameTPYDEmpSalaryParam).Col("formula"),
goqu.T(salaryparamconst.TableNameTPYDEmpSalaryParam).Col("taxstatus"),
goqu.T(salaryparamconst.TableNameTPYDEmpSalaryParam).Col("numdependent"),
goqu.T(salaryparamconst.TableNameTPYDEmpSalaryParam).Col("salnet"),
goqu.T(salaryparamconst.TableNameTPYDEmpSalaryParam).Col("taxed"),
goqu.T(salaryparamconst.TableNameTPYDEmpSalaryParam).Col("taxbornebycomp_dec"),
goqu.T(salaryparamconst.TableNameTPYDEmpSalaryParam).Col("taxfilenumber"),
goqu.T(salaryparamconst.TableNameTPYDEmpSalaryParam).Col("tax_type"),
goqu.T(salaryparamconst.TableNameTPYDEmpSalaryParam).Col("taxlocation_code"),
goqu.T(salaryparamconst.TableNameTPYDEmpSalaryParam).Col("taxpenaltybornebycomp_dec"),
// emp personal
goqu.T(consts.TableNameTEOMEmpPersonal).Col("first_name"),
goqu.T(consts.TableNameTEOMEmpPersonal).Col("middle_name"),
goqu.T(consts.TableNameTEOMEmpPersonal).Col("last_name"),
goqu.T(consts.TableNameTEODEmpCompany).Col("emp_id"),
// emp company
goqu.T(consts.TableNameTEODEmpCompany).Col("work_location_code"),
goqu.T(consts.TableNameTEODEmpCompany).Col("grade_code"),
goqu.T(consts.TableNameTEODEmpCompany).Col("employ_code"),
goqu.T(consts.TableNameTEODEmpCompany).Col("job_status_code"),
goqu.T(consts.TableNameTEODEmpCompany).Col("position_id"),
goqu.T(consts.TableNameTEODEmpCompany).Col("cost_code"),
goqu.T(consts.TableNameTEODEmpCompany).Col("company_id"),
},
Conditions: goqu.And(
goqu.C("company_id").Eq(l.companyID),
goqu.C("emp_id").Eq(req.EmpID),
),
})
err = l.processYtdh(ctx, tx)
if err != nil {
errMsg := fmt.Sprintf("failed to process ytdh: %s", req.EmpID)
wr.Err(errMsg, log.Error(err), log.StackTrace())
return errors.New(errMsg)
}
err = l.processMtdh(ctx, tx)
if err != nil {
errMsg := fmt.Sprintf("failed to process mtdh: %s", req.EmpID)
wr.Err(errMsg, log.Error(err), log.StackTrace())
return errors.New(errMsg)
}
err = l.processMtdcc(ctx, tx)
if err != nil {
errMsg := fmt.Sprintf("failed to process mtdcc: %s", req.EmpID)
wr.Err(errMsg, log.Error(err), log.StackTrace())
return errors.New(errMsg)
}
err = l.processMtdhInsurance(ctx, tx)
if err != nil {
errMsg := fmt.Sprintf("failed to process mtdh insurance: %s", req.EmpID)
wr.Err(errMsg, log.Error(err), log.StackTrace())
return errors.New(errMsg)
}
err = l.processMtddYtdd(ctx, tx)
if err != nil {
errMsg := fmt.Sprintf("failed to process mtddYtdd: %s", req.EmpID)
wr.Err(errMsg, log.Error(err), log.StackTrace())
return errors.New(errMsg)
}
return
}
func (l *coreCalculationLogic) processYtdh(ctx context.Context, tx sfquery.DBExecutor) (err error) {
wr := log.FromCtx(ctx)
wr.Dbg("Process Ytdh Function Called")
//
// Check Proc Ytdh ID on Mtdh based on taxdate
//
l.procYtdhID = ""
getExistYtdhAnotherPeriod, _ := l.vp.Core.Payment.GetProcMTDH(ctx, tx, helperstd.ParamRepository{
Columns: []any{
goqu.I("procytdh_id"),
},
Conditions: goqu.And(
goqu.I("emp_id").Eq(l.req.EmpID),
goqu.L("MONTH(?)", goqu.C("taxdate")).Eq(l.period.Taxdate.Month()),
goqu.L("YEAR(?)", goqu.C("taxdate")).Eq(l.period.Taxdate.Year()),
goqu.I("company_id").Eq(l.companyID),
goqu.I("period_code").Neq(l.req.PeriodCode),
goqu.Or(
goqu.I("terminate_process").Lt(2),
goqu.I("terminate_process").IsNull(),
),
),
})
if util.PointerValue(getExistYtdhAnotherPeriod.ProcytdhID, "") != "" {
l.procYtdhID = util.PointerValue(getExistYtdhAnotherPeriod.ProcytdhID, "")
}
//
// Step 2: Get YTDH data based on conditions
//
//
existYtdh, err := l.getExistYtdh(ctx, tx)
if err != nil {
wr.Err("failed to get exist ytdh", log.Error(err), log.StackTrace())
}
//
// Step 3: Get previous MTDH data if exists (for this period)
//
prevMtdh, err := l.getPrevMtdh(ctx, tx)
if err != nil {
wr.Err("failed to get prev mtdh data", log.Error(err))
}
// Step 4: Check if tax location changed (will be used later)
var taxLocationChanged bool
if existYtdh.ytdTaxLocationCode != l.employeeData.TaxlocationCode {
taxLocationChanged = true
wr.Dbg("tax location changed",
log.Any("tax location status", taxLocationChanged),
log.Any("old tax location code", existYtdh.ytdTaxLocationCode),
log.Any("new tax location code", l.employeeData.TaxlocationCode))
}
//
// Step 5: Calculate Salary
//
err = l.calculateSalaryTax(ctx, tx)
if err != nil {
wr.Err("failed to calculate salary", log.Error(err))
}
needInsertNew := false
var newProcYtdhID string
switch {
case existYtdh.procYtdhID == "":
needInsertNew = true
wr.Dbg("case A: no existing ytdh, will insert new")
// Generate new ProcYtdhID
newProcYtdhID, _ = helperstd.SfGenLogicKey(ctx, tx, helperstd.ParamGenLogicKey{
KeyType: "PROCYTDHID",
EmpId: l.req.EmpID,
CompanyId: l.companyID,
TrxDate: l.period.Taxdate,
PeriodCode: l.period.PeriodCode,
ProcessFlag: "0",
StartPeriod: int(l.period.Taxdate.Month()),
})
// Insert new YTDH with zero values
zeroValueEncrypt := helperstd.SfEncryptCol(ctx, tx, 0, l.req.EmpID)
newYtdh := paymententitystd.ProcYtdh{
ProcytdhID: newProcYtdhID,
Currentyear: int(l.period.Taxdate.Year()),
EmpID: l.req.EmpID,
Taxstatus: util.ToPointer(l.employeeData.Taxstatus),
Numdependent: l.employeeData.Numdependent,
Salnet: l.employeeData.Salnet,
Taxed: util.ToPointer(l.employeeData.Taxed),
TaxbornebycompDec: util.ToPointer(l.employeeData.TaxbornebycompDec),
Taxfilenumber: l.employeeData.Taxfilenumber,
Tax: util.ToPointer(zeroValueEncrypt),
Taxallow: util.ToPointer(zeroValueEncrypt),
Taxbornebycomp: util.ToPointer(zeroValueEncrypt),
Taxbornebygov: util.ToPointer(zeroValueEncrypt),
Taxpenalty: util.ToPointer(zeroValueEncrypt),
Taxpenaltybornebycomp: util.ToPointer(zeroValueEncrypt),
SalaryTax: util.ToPointer(helperstd.SfEncryptCol(ctx, tx, l.SalaryTax, l.req.EmpID)),
CurrencyCodeTax: "THB",
TaxType: l.employeeData.TaxType,
TaxlocationCode: l.employeeData.TaxlocationCode,
CompanyID: l.companyID,
CreatedBy: l.userLoginData.UNAME,
ModifiedBy: l.userLoginData.UNAME,
CreatedDate: l.currentTime,
ModifiedDate: l.currentTime,
}
err = l.vp.Core.Payment.InsertProcYtdh(ctx, tx, []paymententitystd.ProcYtdh{newYtdh})
if err != nil {
return err
}
// Set final ProcYtdhID
l.procYtdhID = newProcYtdhID
case existYtdh.procYtdhID != "" && prevMtdh.procmtdhID != "":
wr.Dbg("case B: Theres existing ytdh and prev mtdh, will update ytdh",
log.Any("exist ytdh", existYtdh),
log.Any("prev mtdh", prevMtdh),
log.Any("struct logic", l),
)
salaryTaxUpdated := existYtdh.SalaryTax - prevMtdh.prevSalaryTax
taxUpdated := existYtdh.Tax - prevMtdh.prevTax
taxAllowUpdated := existYtdh.TaxAllow - prevMtdh.prevTaxAllow
taxBorneByCompUpdated := existYtdh.TaxBorneByComp - prevMtdh.prevTaxBorneByComp
taxBorneByGovUpdated := existYtdh.TaxBorneByGov - prevMtdh.prevTaxBorneByGov
taxPenaltyBorneByComp := existYtdh.TaxPenaltyBorneByComp - prevMtdh.prevTaxPenaltyBorneByComp
taxPenaltyUpdated := existYtdh.TaxPenalty - prevMtdh.prevTaxPenalty
updateYtdh := paymententitystd.ProcYtdh{
Salnet: l.employeeData.Salnet,
Taxed: util.ToPointer(l.employeeData.Taxed),
TaxbornebycompDec: util.ToPointer(l.employeeData.TaxbornebycompDec),
TaxpenaltybornebycompDec: util.ToPointer(l.employeeData.TaxpenaltybornebycompDec),
Taxfilenumber: l.employeeData.Taxfilenumber,
SalaryTax: util.ToPointer(helperstd.SfEncryptCol(ctx, tx, salaryTaxUpdated, l.req.EmpID)),
Tax: util.ToPointer(helperstd.SfEncryptCol(ctx, tx, taxUpdated, l.req.EmpID)),
Taxallow: util.ToPointer(helperstd.SfEncryptCol(ctx, tx, taxAllowUpdated, l.req.EmpID)),
Taxbornebycomp: util.ToPointer(helperstd.SfEncryptCol(ctx, tx, taxBorneByCompUpdated, l.req.EmpID)),
Taxbornebygov: util.ToPointer(helperstd.SfEncryptCol(ctx, tx, taxBorneByGovUpdated, l.req.EmpID)),
Taxpenalty: util.ToPointer(helperstd.SfEncryptCol(ctx, tx, taxPenaltyUpdated, l.req.EmpID)),
Taxpenaltybornebycomp: util.ToPointer(helperstd.SfEncryptCol(ctx, tx, taxPenaltyBorneByComp, l.req.EmpID)),
ModifiedBy: l.userLoginData.UNAME,
ModifiedDate: l.currentTime,
}
updateYtdhConds := goqu.And(
goqu.I("procytdh_id").Eq(existYtdh.procYtdhID),
goqu.I("emp_id").Eq(l.req.EmpID),
goqu.I("company_id").Eq(l.companyID),
)
err = l.vp.Core.Payment.UpdateProcYtdh(ctx, tx, updateYtdh, updateYtdhConds)
if err != nil {
wr.Err("failed update proc ytdh with existing ytdh and prev mtdh", log.Error(err), log.StackTrace())
}
case taxLocationChanged == true:
wr.Dbg("case C: Theres existing ytdh but tax location changed, will insert new ytdh")
// Generate new ProcYtdhID
newProcYtdhId, _ := helperstd.SfGenLogicKey(ctx, tx, helperstd.ParamGenLogicKey{
KeyType: "PROCYTDHID",
EmpId: l.req.EmpID,
CompanyId: l.companyID,
TrxDate: l.period.Taxdate,
PeriodCode: l.period.PeriodCode,
ProcessFlag: "0",
StartPeriod: int(l.period.Taxdate.Month()),
})
l.procYtdhID = newProcYtdhId
}
if l.procYtdhID != "" {
summationSalaryTax := existYtdh.SalaryTax + l.SalaryTax
updateYtdh := paymententitystd.ProcYtdh{
Salnet: l.employeeData.Salnet,
SalaryTax: util.ToPointer(helperstd.SfEncryptCol(ctx, tx, summationSalaryTax, l.req.EmpID)),
ModifiedBy: l.userLoginData.UNAME,
ModifiedDate: l.currentTime,
}
updateYtdhConds := goqu.And(
goqu.I("procytdh_id").Eq(existYtdh.procYtdhID),
goqu.I("emp_id").Eq(l.req.EmpID),
goqu.I("company_id").Eq(l.companyID),
)
err = l.vp.Core.Payment.UpdateProcYtdh(ctx, tx, updateYtdh, updateYtdhConds)
if err != nil {
wr.Err("failed update proc ytdh with existing ytdh and prev mtdh", log.Error(err), log.StackTrace())
}
}
wr.Dbg("processYtdh completed",
log.Any("finalProcYtdhID", l.procYtdhID),
log.Any("needInsertNew", needInsertNew),
log.Any("taxLocationChanged", taxLocationChanged))
return
}
type procYtdhLogic struct {
procYtdhID string
ytdTaxLocationCode string
ytdTaxStatus int
ytdNumDependent float64
ytdTaxType string
ytdCurrNetIncome float64
ytdSalaryTax float64
SalaryTax float64
Tax float64
TaxAllow float64
TaxBorneByComp float64
TaxBorneByGov float64
TaxPenaltyBorneByComp float64
TaxPenalty float64
TaxFix float64
TaxFixNet float64
}
type prevMtdhValues struct {
procmtdhID string
procytdhID string
prevSalary float64
prevSalaryTax float64
prevSalaryPeriod float64
prevTax float64
prevTaxPeriod float64
prevTaxAllow float64
prevTaxAllowPeriod float64
prevTaxBorneByComp float64
prevTaxBorneByCompPeriod float64
prevTaxBorneByGov float64
prevTaxBorneByGovPeriod float64
prevTaxPenaltyBorneByComp float64
prevTaxPenaltyBorneByCompPeriod float64
prevTaxPenalty float64
prevTaxPenaltyPeriod float64
}
func (l *coreCalculationLogic) getExistYtdh(ctx context.Context, tx sfquery.DBExecutor) (res procYtdhLogic, err error) {
wr := log.FromCtx(ctx)
wr.Dbg("getExistYtdh Called - Sub Function of Process YTDH ")
ytdhSelectCol := []any{
goqu.I("procytdh_id"),
goqu.I("taxlocation_code"),
goqu.I("taxstatus"),
goqu.I("numdependent"),
goqu.I("tax_type"),
goqu.I("curr_netincome"),
}
var getExistYtdh paymententitystd.ProcYtdh
if l.procYtdhID != "" {
wr.Dbg("found existing process in same month, using that ytdh", log.Any("procytdh_id", l.procYtdhID))
// CASE: Already processed in same month, follow that YTDH
getExistYtdh, _ = l.vp.Core.Payment.GetProcYTDH(ctx, tx, helperstd.ParamRepository{
Columns: ytdhSelectCol,
Conditions: goqu.And(
goqu.I("emp_id").Eq(l.req.EmpID),
goqu.I("company_id").Eq(l.companyID),
goqu.I("currentyear").Eq(l.period.Taxdate.Year()),
goqu.I("procytdh_id").Eq(l.procYtdhID),
goqu.Or(
goqu.I("terminate_process").Lt(2),
goqu.I("terminate_process").IsNull(),
),
),
})
// -- Append Proc Ytdh ID --
if getExistYtdh.ProcytdhID != "" {
l.procYtdhID = getExistYtdh.ProcytdhID
}
} else {
wr.Dbg("no existing process in same month, get latest ytdh for the year")
// CASE: No process in same month, get the latest YTDH for the year
// This is the subquery logic: select max(procytdh_id)
getExistYtdh, _ = l.vp.Core.Payment.GetProcYTDH(ctx, tx, helperstd.ParamRepository{
Columns: ytdhSelectCol,
Conditions: goqu.And(
goqu.I("emp_id").Eq(l.req.EmpID),
goqu.I("company_id").Eq(l.companyID),
goqu.I("currentyear").Eq(l.period.Taxdate.Year()),
goqu.Or(
goqu.I("terminate_process").Lt(2),
goqu.I("terminate_process").IsNull(),
),
),
Order: []exp.OrderedExpression{goqu.I("procytdh_id").Desc()},
Limit: 1,
})
// -- Append Proc Ytdh ID --
if getExistYtdh.ProcytdhID != "" {
l.procYtdhID = getExistYtdh.ProcytdhID
}
}
if getExistYtdh.ProcytdhID == "" {
wr.Inf("proc ytdh not found", log.Error(err))
return res, nil
}
res.procYtdhID = getExistYtdh.ProcytdhID
res.ytdTaxLocationCode = getExistYtdh.TaxlocationCode
res.ytdTaxStatus = util.PointerValue(getExistYtdh.Taxstatus, 0)
res.ytdNumDependent = util.PointerValue(getExistYtdh.Numdependent, 0.0)
res.ytdTaxType = util.PointerValue(getExistYtdh.TaxType, "")
res.ytdCurrNetIncome = cast.ToFloat64(util.PointerValue(getExistYtdh.CurrNetincome, ""))
// Decrypt columns for detailed values
salaryTaxDecrypt := helperstd.SfDecryptCol(ctx, "salary_tax", l.req.EmpID, "salary_tax")
taxDecrypt := helperstd.SfDecryptCol(ctx, "tax", l.req.EmpID, "tax")
taxAllowDecrypt := helperstd.SfDecryptCol(ctx, "taxallow", l.req.EmpID, "taxallow")
taxBorneByCompDecrypt := helperstd.SfDecryptCol(ctx, "taxbornebycomp", l.req.EmpID, "taxbornebycomp")
taxBorneByGovDecrypt := helperstd.SfDecryptCol(ctx, "taxbornebygov", l.req.EmpID, "taxbornebygov")
taxPenaltyBorneByCompDecrypt := helperstd.SfDecryptCol(ctx, "taxpenaltybornebycomp", l.req.EmpID, "taxpenaltybornebycomp")
taxPenaltyDecrypt := helperstd.SfDecryptCol(ctx, "taxpenalty", l.req.EmpID, "taxpenalty")
taxFixDecypt := helperstd.SfDecryptCol(ctx, "taxfix", l.req.EmpID, "taxfix")
taxFixNetDecypt := helperstd.SfDecryptCol(ctx, "taxfixnet", l.req.EmpID, "taxfixnet")
selectCols := []any{
goqu.I("procytdh_id"),
goqu.I("startp"),
goqu.I("salnet"),
goqu.I("taxstatus"),
goqu.I("tax_type"),
goqu.I("numdependent"),
salaryTaxDecrypt,
taxDecrypt,
taxAllowDecrypt,
taxBorneByCompDecrypt,
taxBorneByGovDecrypt,
taxPenaltyBorneByCompDecrypt,
taxPenaltyDecrypt,
taxFixDecypt,
taxFixNetDecypt,
}
detailProcYtdh, _ := l.vp.Core.Payment.GetProcYTDH(ctx, tx, helperstd.ParamRepository{
Columns: selectCols,
Conditions: goqu.And(
goqu.I("procytdh_id").Eq(l.procYtdhID),
goqu.I("company_id").Eq(l.companyID),
),
})
if detailProcYtdh.ProcytdhID != "" {
res.SalaryTax = cast.ToFloat64(util.PointerValue(detailProcYtdh.SalaryTax, ""))
res.Tax = cast.ToFloat64(util.PointerValue(detailProcYtdh.Tax, ""))
res.TaxAllow = cast.ToFloat64(util.PointerValue(detailProcYtdh.Taxallow, ""))
res.TaxBorneByComp = cast.ToFloat64(util.PointerValue(detailProcYtdh.Taxbornebycomp, ""))
res.TaxBorneByGov = cast.ToFloat64(util.PointerValue(detailProcYtdh.Taxbornebygov, ""))
res.TaxPenaltyBorneByComp = cast.ToFloat64(util.PointerValue(detailProcYtdh.Taxpenaltybornebycomp, ""))
res.TaxPenalty = cast.ToFloat64(util.PointerValue(detailProcYtdh.Taxpenalty, ""))
res.TaxFix = cast.ToFloat64(util.PointerValue(detailProcYtdh.Taxfix, ""))
res.TaxFixNet = cast.ToFloat64(util.PointerValue(detailProcYtdh.TaxFixNet, ""))
}
wr.Dbg("ytdh data retrieved successfully",
log.Any("procYtdhID", res.procYtdhID),
log.Any("taxLocation", res.ytdTaxLocationCode),
log.Any("salaryTax", res.SalaryTax),
log.Any("tax", res.Tax))
return
}
func (l *coreCalculationLogic) getPrevMtdh(ctx context.Context, tx sfquery.DBExecutor) (res prevMtdhValues, err error) {
wr := log.FromCtx(ctx)
wr.Dbg("getPrevMtdh Called - Sub Function of Process YTDH ")
// -- Mapping Decrypted Column --
salaryTaxDecrypt := helperstd.SfDecryptCol(ctx, "salary_tax", l.req.EmpID, "salary_tax")
taxDecrypt := helperstd.SfDecryptCol(ctx, "tax", l.req.EmpID, "tax")
taxPeriodDecrypt := helperstd.SfDecryptCol(ctx, "tax_period", l.req.EmpID, "tax_period")
taxAllowDecrypt := helperstd.SfDecryptCol(ctx, "taxallow", l.req.EmpID, "taxallow")
taxAllowPeriodDecrypt := helperstd.SfDecryptCol(ctx, "taxallow_period", l.req.EmpID, "taxallow_period")
taxBorneByCompDecrypt := helperstd.SfDecryptCol(ctx, "taxbornebycomp", l.req.EmpID, "taxbornebycomp")
taxBorneByCompPeriodDecrypt := helperstd.SfDecryptCol(ctx, "taxbornebycomp_period", l.req.EmpID, "taxbornebycomp_period")
taxBorneByGovDecrypt := helperstd.SfDecryptCol(ctx, "taxbornebygov", l.req.EmpID, "taxbornebygov")
taxBorneByGovPeriodDecrypt := helperstd.SfDecryptCol(ctx, "taxbornebygov_period", l.req.EmpID, "taxbornebygov_period")
taxPenaltyBorneByCompDecrypt := helperstd.SfDecryptCol(ctx, "taxpenaltybornebycomp", l.req.EmpID, "taxpenaltybornebycomp")
taxPenaltyBorneByCompPeriodDecrypt := helperstd.SfDecryptCol(ctx, "taxpenaltybornebycomp_period", l.req.EmpID, "taxpenaltybornebycomp_period")
taxPenaltyDecrypt := helperstd.SfDecryptCol(ctx, "taxpenalty", l.req.EmpID, "taxpenalty")
taxPenaltyPeriodDecrypt := helperstd.SfDecryptCol(ctx, "taxpenalty_period", l.req.EmpID, "taxpenalty_period")
// Get Existing Current Proc Mtdh
existProcMtdh, _ := l.vp.Core.Payment.GetProcMTDH(ctx, tx, helperstd.ParamRepository{
Columns: []any{
goqu.I("procmtdh_id"),
goqu.I("procytdh_id"),
salaryTaxDecrypt,
taxDecrypt,
taxPeriodDecrypt,
taxAllowDecrypt,
taxAllowPeriodDecrypt,
taxBorneByCompDecrypt,
taxBorneByCompPeriodDecrypt,
taxBorneByGovDecrypt,
taxBorneByGovPeriodDecrypt,
taxPenaltyBorneByCompDecrypt,
taxPenaltyBorneByCompPeriodDecrypt,
taxPenaltyDecrypt,
taxPenaltyPeriodDecrypt,
},
Conditions: goqu.And(
goqu.I("emp_id").Eq(l.req.EmpID),
goqu.I("company_id").Eq(l.companyID),
goqu.I("period_code").Eq(l.req.PeriodCode),
goqu.L("MONTH(?)", goqu.C("paydate")).Eq(l.period.Paydate.Month()),
goqu.L("YEAR(?)", goqu.C("paydate")).Eq(l.period.Paydate.Year()),
goqu.Or(
goqu.I("terminate_process").Lt(2),
goqu.I("terminate_process").IsNull(),
),
),
})
wr.Dbg("existing proc mtdh - on prev", log.Response(existProcMtdh))
if existProcMtdh.ProcmtdhID == "" {
// -- Theres no existing Proc Mtdh --
l.procMtdhID = ""
return prevMtdhValues{}, nil
}
prevData := prevMtdhValues{
procmtdhID: existProcMtdh.ProcmtdhID,
procytdhID: util.PointerValue(existProcMtdh.ProcytdhID, ""),
prevSalaryTax: cast.ToFloat64(util.PointerValue(existProcMtdh.SalaryTax, "")),
prevTax: cast.ToFloat64(util.PointerValue(existProcMtdh.Tax, "")),
prevTaxPeriod: cast.ToFloat64(util.PointerValue(existProcMtdh.TaxPeriod, "")),
prevTaxAllow: cast.ToFloat64(util.PointerValue(existProcMtdh.Taxallow, "")),
prevTaxAllowPeriod: cast.ToFloat64(util.PointerValue(existProcMtdh.TaxallowPeriod, "")),
prevTaxBorneByComp: cast.ToFloat64(util.PointerValue(existProcMtdh.Taxbornebycomp, "")),
prevTaxBorneByCompPeriod: cast.ToFloat64(util.PointerValue(existProcMtdh.TaxbornebycompPeriod, "")),
prevTaxBorneByGov: cast.ToFloat64(util.PointerValue(existProcMtdh.Taxbornebygov, "")),
prevTaxBorneByGovPeriod: cast.ToFloat64(util.PointerValue(existProcMtdh.TaxbornebygovPeriod, "")),
prevTaxPenaltyBorneByComp: cast.ToFloat64(util.PointerValue(existProcMtdh.Taxpenaltybornebycomp, "")),
prevTaxPenaltyBorneByCompPeriod: cast.ToFloat64(util.PointerValue(existProcMtdh.TaxpenaltybornebycompPeriod, "")),
prevTaxPenalty: cast.ToFloat64(util.PointerValue(existProcMtdh.Taxpenalty, "")),
prevTaxPenaltyPeriod: cast.ToFloat64(util.PointerValue(existProcMtdh.TaxpenaltyPeriod, "")),
}
// -- Append Current Proc Mtdh ID --
l.procMtdhID = existProcMtdh.ProcmtdhID
wr.Dbg("found previous mtdh data",
log.Any("procmtdh_id", prevData.procmtdhID),
log.Any("prevTax", prevData.prevTax),
log.Any("prev data result", prevData),
)
return prevData, nil
}
func (l *coreCalculationLogic) processMtdh(ctx context.Context, tx sfquery.DBExecutor) (err error) {
wr := log.FromCtx(ctx)
wr.Dbg("Process Mtdh Function Called")
wr.Dbg("debug struct logic on processMtdh", log.Response(l))
taxDate, _ := util.ParseDatePointer(l.period.Taxdate.Format(time.DateTime), time.DateTime)
payDate, _ := util.ParseDatePointer(l.period.Paydate.Format(time.DateTime), time.DateTime)
zeroValueEncrypt := helperstd.SfEncryptCol(ctx, tx, 0, l.req.EmpID)
if l.procMtdhID == "" {
newProcMtdhId, _ := helperstd.SfGenLogicKey(ctx, tx, helperstd.ParamGenLogicKey{
KeyType: "PROCMTDHID",
EmpId: l.req.EmpID,
CompanyId: l.companyID,
TrxDate: l.period.Paydate,
PeriodCode: l.period.PeriodCode,
ProcessFlag: "",
StartPeriod: 0,
})
newMtdh := paymententitystd.ProcMtdh{
ProcmtdhID: newProcMtdhId,
ProcytdhID: util.ToPointer(l.procYtdhID),
PositionID: util.ToPointer(l.employeeData.PositionID),
Salary: util.ToPointer(helperstd.SfEncryptCol(ctx, tx, l.Salary, l.req.EmpID)),
SalaryTax: util.ToPointer(helperstd.SfEncryptCol(ctx, tx, l.SalaryTax, l.req.EmpID)),
SalaryPeriod: util.ToPointer(helperstd.SfEncryptCol(ctx, tx, l.SalaryPeriod, l.req.EmpID)),
TaxbornebycompDec: util.ToPointer(cast.ToString(l.employeeData.TaxbornebycompDec)),
TaxpenaltybornebycompDec: util.ToPointer(cast.ToString(l.employeeData.TaxpenaltybornebycompDec)),
Taxfilenumber: l.employeeData.Taxfilenumber,
CurrencyCode: util.ToPointer(l.employeeData.CurrencyCode),
CurrencyCodeTax: util.ToPointer("THB"),
CurrencyCodePeriod: l.period.CurrencyCode,
Salused: util.ToPointer(l.period.Usesalary),
PeriodCode: l.req.PeriodCode,
EmpID: l.req.EmpID,
Taxdate: taxDate,
Paydate: payDate,
TaxType: l.employeeData.TaxType,
Taxstatus: util.ToPointer(l.employeeData.Taxstatus),
Numdependent: util.ToPointer(cast.ToInt(l.employeeData.Numdependent)),
CompanyID: l.companyID,
CostcenterCode: util.ToPointer(l.employeeData.CostCode),
FirstName: util.ToPointer(l.employeeData.FirstName),
MiddleName: l.employeeData.MIDdleName,
LastName: l.employeeData.LastName,
WorklocationCode: l.employeeData.WorkLocationCode,
TaxlocationCode: l.employeeData.TaxlocationCode,
GradeCode: util.ToPointer(l.employeeData.GradeCode),
EmploymentstatusCode: util.ToPointer(l.employeeData.EmployCode),
Jobstatuscode: l.employeeData.JobStatusCode,
RateTax: util.ToPointer(l.RateTax),
RatePeriod: util.ToPointer(l.RatePeriod),
Taxed: util.ToPointer(l.employeeData.Taxed),
Tax: util.ToPointer(zeroValueEncrypt),
Taxallow: util.ToPointer(zeroValueEncrypt),
Taxbornebygov: util.ToPointer(zeroValueEncrypt),
Taxfix: util.ToPointer(zeroValueEncrypt),
CreatedBy: l.userLoginData.UNAME,
ModifiedBy: util.ToPointer(l.userLoginData.UNAME),
CreatedDate: l.currentTime,
ModifiedDate: util.ToPointer(l.currentTime),
}
err = l.vp.Core.Payment.InsertProcMtdh(ctx, tx, []paymententitystd.ProcMtdh{newMtdh})
if err != nil {
wr.Err("failed to insert new mtdh", log.Error(err), log.StackTrace())
return nil
}
// -- Append Current Mtdh ID --
l.procMtdhID = newProcMtdhId
} else {
updateMtdh := paymententitystd.ProcMtdh{
ProcytdhID: util.ToPointer(l.procYtdhID),
EmpID: l.req.EmpID,
PositionID: util.ToPointer(l.employeeData.PositionID),
CostcenterCode: util.ToPointer(l.employeeData.CostCode),
Salary: util.ToPointer(helperstd.SfEncryptCol(ctx, tx, l.Salary, l.req.EmpID)),
SalaryTax: util.ToPointer(helperstd.SfEncryptCol(ctx, tx, l.SalaryTax, l.req.EmpID)),
SalaryPeriod: util.ToPointer(helperstd.SfEncryptCol(ctx, tx, l.SalaryPeriod, l.req.EmpID)),
TaxbornebycompDec: util.ToPointer(cast.ToString(l.employeeData.TaxbornebycompDec)),
TaxpenaltybornebycompDec: util.ToPointer(cast.ToString(l.employeeData.TaxpenaltybornebycompDec)),
CurrencyCode: util.ToPointer(l.employeeData.CurrencyCode),
CurrencyCodeTax: util.ToPointer("THB"),
CurrencyCodePeriod: l.period.CurrencyCode,
Salused: util.ToPointer(l.period.Usesalary),
Taxdate: taxDate,
Paydate: payDate,
TaxType: l.employeeData.TaxType,
Taxfilenumber: l.employeeData.Taxfilenumber,
Taxstatus: util.ToPointer(l.employeeData.Taxstatus),
Numdependent: util.ToPointer(cast.ToInt(l.employeeData.Numdependent)),
FirstName: util.ToPointer(l.employeeData.FirstName),
MiddleName: l.employeeData.MIDdleName,
LastName: l.employeeData.LastName,
WorklocationCode: l.employeeData.WorkLocationCode,
TaxlocationCode: l.employeeData.TaxlocationCode,
GradeCode: util.ToPointer(l.employeeData.GradeCode),
EmploymentstatusCode: util.ToPointer(l.employeeData.EmployCode),
Jobstatuscode: l.employeeData.JobStatusCode,
RateTax: util.ToPointer(l.RateTax),
RatePeriod: util.ToPointer(l.RatePeriod),
Taxed: util.ToPointer(l.employeeData.Taxed),
Tax: util.ToPointer(zeroValueEncrypt),
Taxallow: util.ToPointer(zeroValueEncrypt),
Taxbornebygov: util.ToPointer(zeroValueEncrypt),
Taxfix: util.ToPointer(zeroValueEncrypt),
Taxfixnet: util.ToPointer(zeroValueEncrypt),
ModifiedBy: util.ToPointer(l.userLoginData.UNAME),
ModifiedDate: util.ToPointer(l.currentTime),
}
updateMtdhConds := goqu.And(
goqu.I("procmtdh_id").Eq(l.procMtdhID),
goqu.I("company_id").Eq(l.companyID),
goqu.I("emp_id").Eq(l.req.EmpID),
)
err = l.vp.Core.Payment.UpdateProcMtdh(ctx, tx, updateMtdh, updateMtdhConds)
if err != nil {
wr.Err("failed to update proc mtdh", log.Error(err), log.StackTrace())
return nil
}
//
// Update Proc Ytdh (Recalculate)
//
updateYtdh := paymententitystd.ProcYtdh{
Salnet: l.employeeData.Salnet,
Taxed: util.ToPointer(l.employeeData.Taxed),
TaxbornebycompDec: util.ToPointer(l.employeeData.TaxbornebycompDec),
TaxpenaltybornebycompDec: util.ToPointer(l.employeeData.TaxpenaltybornebycompDec),
Taxfilenumber: l.employeeData.Taxfilenumber,
SalaryTax: util.ToPointer(helperstd.SfEncryptCol(ctx, tx, l.SalaryTax, l.req.EmpID)),
ModifiedBy: l.userLoginData.UNAME,
ModifiedDate: l.currentTime,
}
wr.Dbg("update proc ytdh after prorcess mtdh",
log.Any("struct update", updateYtdh),
log.Any("salary tax", l.SalaryTax),
)
updateYtdhConds := goqu.And(
goqu.I("procytdh_id").Eq(l.procYtdhID),
goqu.I("emp_id").Eq(l.req.EmpID),
goqu.I("company_id").Eq(l.companyID),
)
err = l.vp.Core.Payment.UpdateProcYtdh(ctx, tx, updateYtdh, updateYtdhConds)
if err != nil {
wr.Err("failed update proc ytdh after prorcess mtdh", log.Error(err), log.StackTrace())
}
}
return
}
func (l *coreCalculationLogic) calculateSalaryTax(ctx context.Context, tx sfquery.DBExecutor) (err error) {
wr := log.FromCtx(ctx)
wr.Dbg("calculateSalaryTax Called - Sub Function of Process YTDH")
// -- Return Default Value If Not Usesalary --
l.Salary = 0
l.SalaryTax = 0
l.SalaryPeriod = 0
l.RateTax = 1
l.RatePeriod = 1
if l.period.Usesalary != "Y" {
return nil
}
// -- Get New Salary --
newSalaryDecrypt := helperstd.SfDecryptCol(ctx, "new_salary", l.req.EmpID, "new_salary")
salaryParamTemp, _ := l.vp.Core.SalaryParam.GetEmpSalaryParamTemp(ctx, tx, helperstd.ParamRepository{
Columns: []any{
newSalaryDecrypt,
},
Conditions: goqu.And(
goqu.I("emp_id").Eq(l.req.EmpID),
goqu.I("company_id").Eq(l.companyID),
goqu.I("period").Eq(l.req.PeriodCode),
),
})
salaryTemp := cast.ToFloat64(salaryParamTemp.NewSalary)
l.Salary = salaryTemp
l.SalaryTax = salaryTemp
l.SalaryPeriod = salaryTemp
if l.employeeData.CurrencyCode != "THB" {
l.Salary, _ = helperstd.SfCurrencyRound(ctx, tx, salaryTemp, "THB")
convertSalaryTax, _ := helperstd.SfConvertCurrency(ctx, tx, l.Salary, l.employeeData.CurrencyCode, "THB", l.period.Taxdate, "TAX")
l.SalaryTax, _ = helperstd.SfCurrencyRound(ctx, tx, convertSalaryTax.ConvertResult, "THB")
l.RateTax = convertSalaryTax.ConvertRate
}
if l.employeeData.CurrencyCode != util.PointerValue(l.period.CurrencyCode, "") {
convertSalaryPeriod, _ := helperstd.SfConvertCurrency(ctx, tx, salaryTemp, l.employeeData.CurrencyCode, *l.period.CurrencyCode, l.period.Paydate, "TRANSFER")
l.SalaryPeriod, _ = helperstd.SfCurrencyRound(ctx, tx, convertSalaryPeriod.ConvertResult, "THB")
l.RatePeriod = convertSalaryPeriod.ConvertRate
}
wr.Dbg("Salary Param Temp - Final",
log.Any("final salary", l.Salary),
log.Any("final salary tax", l.Salary),
log.Any("final salary period", l.Salary),
)
return
}
func (l *coreCalculationLogic) processMtdcc(ctx context.Context, tx sfquery.DBExecutor) (err error) {
wr := log.FromCtx(ctx)
procMtdh, _ := l.vp.Core.Payment.GetProcMTDH(ctx, tx, helperstd.ParamRepository{
Columns: []any{
goqu.I("procmtdh_id"),
},
Conditions: goqu.And(
goqu.I("emp_id").Eq(l.req.EmpID),
goqu.I("company_id").Eq(l.companyID),
goqu.I("procmtdh_id").Eq(l.procMtdhID),
),
})
//
// Delete Existing Proc Mtdcc
//
if procMtdh.ProcmtdhID != "" {
deleteConds := goqu.And(
goqu.I("procmtdh_id").Eq(procMtdh.ProcmtdhID),
goqu.I("company_id").Eq(l.companyID),
)
err = l.vp.Core.Payment.DeleteProcMtdcc(ctx, tx, deleteConds)
if err != nil {
wr.Err(consts.LogMessageDBFailedToDelete, log.Error(err), log.StackTrace())
return err
}
}
empCostCenters, totalEmpCostCenter, _ := l.vp.Core.CostCenter.GetEmpCcAllocations(ctx, tx, helperstd.ParamRepository{
Columns: []any{
goqu.I("effective_date"),
goqu.I("emp_id"),
goqu.I("company_id"),
},
Conditions: goqu.And(
goqu.I("emp_id").Eq(l.req.EmpID),
goqu.I("company_id").Eq(l.companyID),
goqu.I("effective_date").Lte(l.period.Paydate),
),
Order: []exp.OrderedExpression{
goqu.I("effective_date").Desc(),
},
})
costCenterPctToInserted := []paymententitystd.ProcMtdcc{}
if totalEmpCostCenter > 0 {
firstEffDate := empCostCenters[0].EffectiveDate
empCostCenters, totalEmpCostCenter, _ := l.vp.Core.CostCenter.GetEmpCcAllocations(ctx, tx, helperstd.ParamRepository{
Columns: []any{
goqu.I("costcenter_code"),
goqu.I("percentage"),
goqu.I("emp_id"),
goqu.I("company_id"),
goqu.I("effective_date"),
},
Conditions: goqu.And(
goqu.I("emp_id").Eq(l.req.EmpID),
goqu.I("company_id").Eq(l.companyID),
goqu.I("effective_date").Eq(firstEffDate),
),
})
if totalEmpCostCenter > 0 {
for _, v := range empCostCenters {
costCenterPctToInserted = append(costCenterPctToInserted, paymententitystd.ProcMtdcc{
ProcmtdhID: procMtdh.ProcmtdhID,
CompanyID: l.companyID,
CostcenterCode: v.CostcenterCode,
Percentage: cast.ToInt(v.Percentage),
CreatedBy: l.userLoginData.UNAME,
CreatedDate: l.currentTime,
ModifiedBy: l.userLoginData.UNAME,
ModifiedDate: l.currentTime,
})
}
}
} else {
empCompanies, totalEmpCompany, _ := l.vp.Core.Employee.GetEmployeeCompanies(ctx, tx, helperstd.ParamRepository{
Columns: []any{
goqu.I("cost_code"),
goqu.I("emp_id"),
goqu.I("company_id"),
},
Conditions: goqu.And(
goqu.I("emp_id").Eq(l.req.EmpID),
goqu.I("company_id").Eq(l.companyID),
),
})
if totalEmpCompany > 0 {
for _, v := range empCompanies {
costCenterPctToInserted = append(costCenterPctToInserted, paymententitystd.ProcMtdcc{
ProcmtdhID: procMtdh.ProcmtdhID,
CompanyID: l.companyID,
CostcenterCode: v.CostCode,
Percentage: 100,
CreatedBy: l.userLoginData.UNAME,
CreatedDate: l.currentTime,
ModifiedBy: l.userLoginData.UNAME,
ModifiedDate: l.currentTime,
})
}
}
}
//
// Insert Proc Mtdcc
//
if len(costCenterPctToInserted) > 0 {
err = l.vp.Core.Payment.InsertProcMtdcc(ctx, tx, costCenterPctToInserted)
if err != nil {
wr.Err(consts.LogMessageDBFailedToInsert, log.Error(err), log.StackTrace())
return err
}
}
return
}
func (l *coreCalculationLogic) processMtdhInsurance(ctx context.Context, tx sfquery.DBExecutor) (err error) {
wr := log.FromCtx(ctx)
procMtdh, _ := l.vp.Core.Payment.GetProcMTDH(ctx, tx, helperstd.ParamRepository{
Columns: []any{
goqu.I("procmtdh_id"),
},
Conditions: goqu.And(
goqu.I("emp_id").Eq(l.req.EmpID),
goqu.I("company_id").Eq(l.companyID),
goqu.I("procmtdh_id").Eq(l.procMtdhID),
),
})
//
// Delete Existing Proc Mtdh Insurance
//
if procMtdh.ProcmtdhID != "" {
deleteConds := goqu.And(
goqu.I("procmtdh_id").Eq(procMtdh.ProcmtdhID),
goqu.I("company_id").Eq(l.companyID),
)
err = l.vp.Core.Payment.DeleteProcMtdhInsurance(ctx, tx, deleteConds)
if err != nil {
wr.Err(consts.LogMessageDBFailedToDelete, log.Error(err), log.StackTrace())
return err
}
}
salaryInsurances, totalSalaryInsurance, _ := l.vp.Core.SalaryParam.GetEmpSalaryInsurance(ctx, tx, helperstd.ParamRepository{
Columns: []any{
goqu.I("insurance_code"),
goqu.I("insurance_no"),
goqu.I("emp_id"),
goqu.I("company_id"),
},
Conditions: goqu.And(
goqu.I("emp_id").Eq(l.req.EmpID),
goqu.I("company_id").Eq(l.companyID),
),
})
if totalSalaryInsurance == 0 {
return nil
}
empSalaryInsuranceToInserted := []paymententitystd.ProcMtdhInsurance{}
for _, v := range salaryInsurances {
newProcMtdhInsurance := paymententitystd.ProcMtdhInsurance{
ProcmtdhID: procMtdh.ProcmtdhID,
CompanyID: l.companyID,
InsuranceCode: v.InsuranceCode,
InsuranceNo: util.PointerValue(v.InsuranceNo, ""),
}
empSalaryInsuranceToInserted = append(empSalaryInsuranceToInserted, newProcMtdhInsurance)
}
err = l.vp.Core.Payment.InsertProcMtdhInsurance(ctx, tx, empSalaryInsuranceToInserted)
if err != nil {
wr.Err(consts.LogMessageDBFailedToInsert, log.Error(err), log.StackTrace())
return err
}
return
}
func (l *coreCalculationLogic) processMtddYtdd(ctx context.Context, tx sfquery.DBExecutor) (err error) {
wr := log.FromCtx(ctx)
wr.Dbg("Process Mtdd & Ytdd Caled")
procMtdh, _ := l.vp.Core.Payment.GetProcMTDH(ctx, tx, helperstd.ParamRepository{
Columns: []any{
goqu.I("procmtdh_id"),
goqu.I("procytdh_id"),
goqu.I("period_code"),
goqu.I("currency_code"),
goqu.I("currency_code_tax"),
goqu.I("currency_code_period"),
goqu.I("paydate"),
goqu.I("taxdate"),
},
Conditions: goqu.And(
goqu.I("emp_id").Eq(l.req.EmpID),
goqu.I("company_id").Eq(l.companyID),
goqu.I("procmtdh_id").Eq(l.procMtdhID),
),
})
wr.Dbg("procmtdh in mtdd", log.Response(procMtdh))
if procMtdh.ProcmtdhID == "" {
return nil
}
compValueTaxDecCol := helperstd.SfDecryptCol(ctx, "comp_value_tax", l.req.EmpID, "comp_value_tax")
procMtdd, totalProcMtdd, _ := l.vp.Core.Payment.GetProcMTDDs(ctx, tx, helperstd.ParamRepository{
Columns: []any{
goqu.I("allowdeduct_code"),
goqu.I("procmtdh_id"),
goqu.I("company_id"),
compValueTaxDecCol,
},
Conditions: goqu.And(
goqu.I("procmtdh_id").Eq(l.procMtdhID),
goqu.I("company_id").Eq(l.companyID),
),
})
wr.Dbg("existing proc mtdd", log.Response(procMtdd))
type debugStruct struct {
ValueMtdd float64
ValueYtddExist float64
ValueYtddAFterSubstract float64
}
var arrDebug []debugStruct
if totalProcMtdd > 0 {
// 1. Update YTDD by substraction existing comp value tax with current comp value tax
// 2. Delete Current Proc MTDD to be re-process
compsToBeDeleted := []string{}
for _, v := range procMtdd {
// -- Append Component MTDD to be deleted --
compsToBeDeleted = append(compsToBeDeleted, v.AllowdeductCode)
//
// Get Existing YTDD
//
compValueTaxYtddCol := helperstd.SfDecryptCol(ctx, "comp_value_tax", l.req.EmpID, "comp_value_tax")
procYtdd, _ := l.vp.Core.Payment.GetProcYTDD(ctx, tx, helperstd.ParamRepository{
Columns: []any{
compValueTaxYtddCol,
goqu.I("procytdh_id"),
goqu.I("allowdeduct_code"),
goqu.I("company_id"),
},
Conditions: goqu.And(
goqu.I("procytdh_id").Eq(procMtdh.ProcytdhID),
goqu.I("allowdeduct_code").Eq(v.AllowdeductCode),
goqu.I("company_id").Eq(l.companyID),
),
})
if procYtdd.ProcytdhID == "" {
continue
}
//
// Substract Existing YTDD With Current MTDD comp value tax to be re-processed
//
compValueTaxYtdd := cast.ToFloat64(procYtdd.CompValueTax)
compValueTaxMtdd := cast.ToFloat64(v.CompValueTax)
sbtrCompValueTaxYtdd := compValueTaxYtdd - compValueTaxMtdd
sbtrCompValueTaxYtddEnc := helperstd.SfEncryptCol(ctx, tx, sbtrCompValueTaxYtdd, l.req.EmpID)
arrDebug = append(arrDebug, debugStruct{
ValueMtdd: compValueTaxMtdd,
ValueYtddExist: compValueTaxYtdd,
ValueYtddAFterSubstract: sbtrCompValueTaxYtdd,
})
//
// Update Proc YTDD
//
updateProcYtdd := entity.ProcYtdd{
CompValueTax: util.ToPointer(sbtrCompValueTaxYtddEnc),
}
updateProcYtddConds := goqu.And(
goqu.I("procytdh_id").Eq(procYtdd.ProcytdhID),
goqu.I("allowdeduct_code").Eq(v.AllowdeductCode),
goqu.I("company_id").Eq(l.companyID),
)
err = l.vp.Core.Payment.UpdateProcYtdd(ctx, tx, updateProcYtdd, updateProcYtddConds)
if err != nil {
wr.Err(consts.LogMessageDBFailedToUpdate, log.Error(err), log.StackTrace())
return err
}
}
wr.Dbg("compsToBeDeleted in mtdd", log.Response(compsToBeDeleted))
//
// Delete Existing Proc MTDD
//
if len(compsToBeDeleted) > 0 {
wr.Dbg("component to be deleted", log.Response(compsToBeDeleted))
delExistMtddConds := goqu.And(
goqu.I("procmtdh_id").Eq(l.procMtdhID),
goqu.I("allowdeduct_code").In(compsToBeDeleted),
goqu.I("company_id").Eq(l.companyID),
)
err = l.vp.Core.Payment.DeleteProcMtdd(ctx, tx, delExistMtddConds)
if err != nil {
wr.Err(consts.LogMessageDBFailedToDelete, log.Error(err), log.StackTrace())
return err
}
}
}
wr.Dbg("arrDebug", log.Response(arrDebug))
// Process MTDD
// 1. Get Payroll Component From TPYDEMPALLOWDUDCT JOIN TPYMPAYALLOWDEDUCT
// 2. If Exist Process Comp Value, Comp Value Tax & Comp Value Period Per Component
// 3. Insert to ProcMTDD, which has been deleted previously
// 4. Insert to ProcYTDD if not exist / Update If Exist with summation comp value tax existing & current
// process
allowDeductValueCol := helperstd.SfDecryptCol(ctx, "allowdeduct_value", l.req.EmpID, "allowdeduct_value")
formulaResultCol := helperstd.SfDecryptCol(ctx, "formula_result", l.req.EmpID, "formula_result")
payComps, totalPayComp, _ := l.vp.Core.Component.GetEmpPayAllowDeduct(ctx, tx, helperstd.ParamRepository{
Columns: []any{
allowDeductValueCol,
formulaResultCol,
goqu.T(componentstdconst.TableNameTPYDEmpAllowDeduct).Col("emp_id"),
goqu.T(componentstdconst.TableNameTPYDEmpAllowDeduct).Col("period_code"),
goqu.T(componentstdconst.TableNameTPYDEmpAllowDeduct).Col("company_id"),
goqu.T(componentstdconst.TableNameTPYDEmpAllowDeduct).Col("allowdeduct_code"),
goqu.T(componentstdconst.TableNameTPYDEmpAllowDeduct).Col("currency_code"),
goqu.T(componentstdconst.TableNameTPYDEmpAllowDeduct).Col("taxed"),
goqu.T(componentstdconst.TableNameTPYDEmpAllowDeduct).Col("net"),
goqu.T(componentstdconst.TableNameTPYDEmpAllowDeduct).Col("taxclass"),
goqu.T(componentstdconst.TableNameTPYDEmpAllowDeduct).Col("allowdeduct_formula"),
goqu.T(componentstdconst.TableNameTPYDEmpAllowDeduct).Col("fixed"),
goqu.T(componentstdconst.TableNameTPYMPayAllowDeduct).Col("enabled"),
goqu.T(componentstdconst.TableNameTPYMPayAllowDeduct).Col("allowdeducttype"),
goqu.T(componentstdconst.TableNameTPYMPayAllowDeduct).Col("allowdeductname_en"),
goqu.T(componentstdconst.TableNameTPYMPayAllowDeduct).Col("allowdeductname_id"),
goqu.T(componentstdconst.TableNameTPYMPayAllowDeduct).Col("allowdeductname_my"),
goqu.T(componentstdconst.TableNameTPYMPayAllowDeduct).Col("allowdeductname_th"),
},
Conditions: goqu.And(
goqu.C("emp_id").Eq(l.req.EmpID),
goqu.C("period_code").Eq(l.req.PeriodCode),
goqu.C("company_id").Eq(l.companyID),
goqu.C("enabled").Eq("Y"),
),
})
wr.Dbg("payroll component", log.Response(payComps))
if totalPayComp == 0 {
msg := fmt.Sprintf("no component found for employee %s", l.req.EmpID)
wr.Inf(msg, log.Any("payroll component", payComps))
return nil
}
var takeHomePayTax, takeHomePayPeriod float64
mtddToBeInserteds := []entity.ProcMtdd{}
ytddToBeInserteds := []entity.ProcYtdd{}
currencyCodeTax := util.PointerValue(procMtdh.CurrencyCodeTax, "")
currencyPeriod := util.PointerValue(l.period.CurrencyCode, "")
type DebugProcssMtddYtdd struct {
AllowdeductCode string
CompValueTaxMtdd float64
CompValueTaxYtddExisting float64
SummationForUpdateYtdd float64
}
var arrDebugProcssMtddYtdd []DebugProcssMtddYtdd
for _, comp := range payComps {
formulaResult := cast.ToFloat64(comp.FormulaResult)
currencyComp := util.PointerValue(comp.CurrencyCode, "")
compValue, _ := helperstd.SfCurrencyRound(ctx, tx, formulaResult, currencyComp)
// If component value is 0, skip
if compValue == 0 {
continue
}
//
// Handle If Currency Code Component Is Different With Currency Code Tax
//
var compValueTax float64
compValueTax = compValue
if currencyComp != currencyCodeTax {
// Convert Result Based On Type
convertResult, err := helperstd.SfConvertCurrency(ctx, tx, formulaResult, currencyComp, currencyCodeTax, *procMtdh.Taxdate, "TAX")
if err != nil {
wr.Err("failed to convert currency on value tax", log.Error(err), log.StackTrace())
return nil
}
wr.Dbg("masuk kondisi convert currency", log.Response(convertResult))
compValueTax = convertResult.ConvertResult
// rateTax = convertResult.ConvertRate
// Rounding Comp Value Tax
compValueTax, err = helperstd.SfCurrencyRound(ctx, tx, compValueTax, currencyCodeTax)
if err != nil {
wr.Err("failed to round comp value tax", log.Error(err), log.StackTrace())
return nil
}
}
//
// Handle If Currency Code Component Is Differenct With Period Currency Code
//
var compValuePeriod float64
compValuePeriod = compValue
if currencyComp != currencyPeriod {
// Convert Result Based On Type
convertResult, err := helperstd.SfConvertCurrency(ctx, tx, formulaResult, util.PointerValue(comp.CurrencyCode, ""), util.PointerValue(procMtdh.CurrencyCodeTax, ""), *procMtdh.Paydate, "TRANSFER")
if err != nil {
wr.Err("failed to convert currency on value period", log.Error(err), log.StackTrace())
return nil
}
compValuePeriod = convertResult.ConvertResult
// rateTaxPeriod = convertResult.ConvertRate
// Rounding Comp Value Tax
compValuePeriod, err = helperstd.SfCurrencyRound(ctx, tx, compValuePeriod, util.PointerValue(procMtdh.CurrencyCodeTax, ""))
if err != nil {
wr.Err("failed to round comp value period", log.Error(err), log.StackTrace())
return nil
}
}
//
// Append MTDD To Be Inserted
//
compValueEnc := helperstd.SfEncryptCol(ctx, tx, compValue, l.req.EmpID)
compValueTaxEnc := helperstd.SfEncryptCol(ctx, tx, compValueTax, l.req.EmpID)
compValuePeriodEnc := helperstd.SfEncryptCol(ctx, tx, compValuePeriod, l.req.EmpID)
newMtdd := entity.ProcMtdd{
ProcmtdhID: l.procMtdhID,
CompanyID: l.companyID,
AllowdeductCode: comp.AllowdeductCode,
Allowdeducttype: util.ToPointer(comp.AllowDeductType),
AllowdeductnameEn: util.ToPointer(comp.AllowDeductNameEN),
AllowdeductnameId: comp.AllowDeductNameID,
AllowdeductnameMy: comp.AllowDeductNameMY,
AllowdeductnameTh: comp.AllowDeductNameTH,
CurrencyCode: util.PointerValue(comp.CurrencyCode, ""),
CurrencyCodeTax: util.PointerValue(procMtdh.CurrencyCodeTax, ""),
CurrencyCodePeriod: util.PointerValue(l.period.CurrencyCode, ""),
CompValue: util.ToPointer(compValueEnc),
CompValueTax: util.ToPointer(compValueTaxEnc),
CompValuePeriod: util.ToPointer(compValuePeriodEnc),
AllowdeductValue: util.ToPointer(comp.AllowdeductValue),
AllowdeductFormula: util.ToPointer(comp.AllowdeductFormula),
Taxed: util.ToPointer(comp.Taxed),
Net: util.ToPointer(comp.Net),
Taxclass: comp.Taxclass,
Fixed: comp.Fixed,
}
if newMtdd != (entity.ProcMtdd{}) {
mtddToBeInserteds = append(mtddToBeInserteds, newMtdd)
}
//
// Check Existing YTDD
//
compValueTaxYtddCol := helperstd.SfDecryptCol(ctx, "comp_value_tax", l.req.EmpID, "comp_value_tax")
getYtdd, _ := l.vp.Core.Payment.GetProcYTDD(ctx, tx, helperstd.ParamRepository{
Columns: []any{
compValueTaxYtddCol,
goqu.I("procytdh_id"),
goqu.I("allowdeduct_code"),
goqu.I("company_id"),
},
Conditions: goqu.And(
goqu.I("procytdh_id").Eq(procMtdh.ProcytdhID),
goqu.I("allowdeduct_code").Eq(comp.AllowdeductCode),
goqu.I("company_id").Eq(l.companyID),
),
})
//
// Insert / Update YTDD
//
if getYtdd.ProcytdhID != "" {
compValueTaxFloat := cast.ToFloat64(getYtdd.CompValueTax)
sumCompValueTax := compValueTaxFloat + compValueTax
compValueTaxYtddEnc := helperstd.SfEncryptCol(ctx, tx, sumCompValueTax, l.req.EmpID)
arrDebugProcssMtddYtdd = append(arrDebugProcssMtddYtdd, DebugProcssMtddYtdd{
AllowdeductCode: comp.AllowdeductCode,
CompValueTaxMtdd: compValueTax,
CompValueTaxYtddExisting: compValueTaxFloat,
SummationForUpdateYtdd: sumCompValueTax,
})
updateYtdd := entity.ProcYtdd{
CompValueTax: util.ToPointer(compValueTaxYtddEnc),
Taxed: comp.Taxed,
Net: comp.Net,
Taxclass: comp.Taxclass,
CurrencyCodeTax: util.PointerValue(procMtdh.CurrencyCodeTax, ""),
AllowdeductnameEn: util.ToPointer(comp.AllowDeductNameEN),
AllowdeductnameId: comp.AllowDeductNameID,
AllowdeductnameMy: comp.AllowDeductNameMY,
AllowdeductnameTh: comp.AllowDeductNameTH,
Allowdeducttype: util.ToPointer(comp.AllowDeductType),
Fixed: comp.Fixed,
}
updateYtddConds := goqu.And(
goqu.I("procytdh_id").Eq(procMtdh.ProcytdhID),
goqu.I("allowdeduct_code").Eq(comp.AllowdeductCode),
goqu.I("company_id").Eq(l.companyID),
)
err = l.vp.Core.Payment.UpdateProcYtdd(ctx, tx, updateYtdd, updateYtddConds)
if err != nil {
wr.Err("failed to update proc ytdd", log.Error(err), log.StackTrace())
return nil
}
} else {
//
// Append YTDD To Be Inserted
//
newYtdd := entity.ProcYtdd{
ProcytdhID: l.procYtdhID,
CompanyID: l.companyID,
AllowdeductCode: comp.AllowdeductCode,
CompValueTax: util.ToPointer(compValueTaxEnc),
Taxed: comp.Taxed,
Net: comp.Net,
Taxclass: comp.Taxclass,
CurrencyCodeTax: util.PointerValue(procMtdh.CurrencyCodeTax, ""),
AllowdeductnameEn: util.ToPointer(comp.AllowDeductNameEN),
AllowdeductnameId: comp.AllowDeductNameID,
AllowdeductnameMy: comp.AllowDeductNameMY,
AllowdeductnameTh: comp.AllowDeductNameTH,
Allowdeducttype: util.ToPointer(comp.AllowDeductType),
Fixed: comp.Fixed,
}
ytddToBeInserteds = append(ytddToBeInserteds, newYtdd)
}
switch comp.AllowDeductType {
case "A":
takeHomePayTax += compValueTax
takeHomePayPeriod += compValuePeriod
case "D":
takeHomePayTax -= compValueTax
takeHomePayPeriod -= compValuePeriod
}
}
wr.Dbg("arrDebugProcssMtddYtdd", log.Response(arrDebugProcssMtddYtdd))
wr.Dbg(
"proc mtdd & ytdd to be inserted",
log.Any("mtdd to be inserted", mtddToBeInserteds),
log.Any("panjang mtdd", len(mtddToBeInserteds)),
log.Any("ytdd to be inserted", ytddToBeInserteds),
log.Any("takeHomePayTax", takeHomePayTax),
log.Any("takeHomePayPeriod", takeHomePayPeriod),
)
if len(mtddToBeInserteds) > 0 {
for _, v := range mtddToBeInserteds {
err = l.vp.Core.Payment.InsertProcMtdd(ctx, tx, []entity.ProcMtdd{v})
if err != nil {
wr.Err("failed to insert proc mtdd", log.Error(err), log.StackTrace())
return nil
}
}
}
if len(ytddToBeInserteds) > 0 {
for _, v := range ytddToBeInserteds {
err = l.vp.Core.Payment.InsertProcYtdd(ctx, tx, []entity.ProcYtdd{v})
if err != nil {
wr.Err("failed to insert proc ytdd", log.Error(err), log.StackTrace())
return nil
}
}
}
///
// Update THP Tax & Thp Period
//
takeHomePayTax += cast.ToFloat64(l.SalaryTax)
takeHomePayPeriod += cast.ToFloat64(l.SalaryPeriod)
updateProcMtdh := entity.ProcMtdh{
ThpTax: util.ToPointer(helperstd.SfEncryptCol(ctx, tx, takeHomePayTax, l.req.EmpID)),
ThpPeriod: util.ToPointer(helperstd.SfEncryptCol(ctx, tx, takeHomePayPeriod, l.req.EmpID)),
PositionID: util.ToPointer(l.employeeData.PositionID),
}
updateMtdhConds := goqu.And(
goqu.I("procmtdh_id").Eq(l.procMtdhID),
goqu.I("company_id").Eq(l.companyID),
goqu.I("emp_id").Eq(l.req.EmpID),
)
err = l.vp.Core.Payment.UpdateProcMtdh(ctx, tx, updateProcMtdh, updateMtdhConds)
if err != nil {
wr.Err("failed to update proc mtdh", log.Error(err), log.StackTrace())
return nil
}
return
}
Editor is loading...
Leave a Comment