| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Linq;
- using PX.Common;
- using PX.Data;
- using PX.Objects.CM;
- using PX.Objects.GL;
- using PX.Objects.IN;
- using PX.Objects.PM;
- namespace ProjectEntryLaborAdjust
- {
-
- public class ProjectEntryExt : PXGraphExtension<ProjectEntry>
- {
- private const string SlotLaborUpdate = "RevBudSetLabor"; // <-- new flag
- private const string LaborInvCD = "L023";
- private const string ExcludedBranchCD = "301";
- private const string ExcludedBillRule = "PROGRESS";
- private const decimal PMRate = 90m;
- private int? _laborInvID;
- private int? LaborInvID
- {
- get
- {
- if (_laborInvID == null)
- _laborInvID = InventoryItem.UK.Find(Base, LaborInvCD)?.InventoryID;
- return _laborInvID;
- }
- }
- protected bool Skip => Base.IsImport || Base.IsContractBasedAPI || Base.IsCopyPasteContext;
- protected void _(Events.RowUpdated<PMRevenueBudget> e)
- {
- if (IsExcludedProject()) // ← add this line
- return;
- var row = (PMRevenueBudget)e.Row;
- var old = (PMRevenueBudget)e.OldRow;
- if (row == null || old == null || Skip) return;
- // Prevent recursion
- if (PXContext.GetSlot<bool?>(SlotLaborUpdate) == true) return;
- // Only react when Original Budgeted Amount changed
- if (e.Cache.ObjectsEqual<PMRevenueBudget.curyAmount>(row, old))
- {
- return;
- }
- // Ignore if user is editing the labor line itself
- if (LaborInvID != null && row.InventoryID == LaborInvID) return;
- // ---- Calculate totals/hours (exclude labor line) -----------------
- decimal totalRevised = GetRevisedRevenueTotalUI(LaborInvCD);
- decimal pmHours = GetPMHours(totalRevised);
- PXContext.SetSlot<bool?>(SlotLaborUpdate, true);
- try
- {
- var cache = Base.RevenueBudget.Cache;
- PMRevenueBudget laborLine = FindRevenueLineByInventoryCd(LaborInvCD);
-
- if (laborLine != null)
- {
- var c = Base.RevenueBudget.Cache;
- if (laborLine.Qty != pmHours)
- c.SetValueExt<PMRevenueBudget.qty>(laborLine, pmHours);
- if (laborLine.CuryUnitRate != PMRate)
- c.SetValueExt<PMRevenueBudget.curyUnitRate>(laborLine, PMRate);
- // DO NOT set curyAmount – let the formula do it.
- c.Update(laborLine);
- }
- else
- {
- CreateRevenueBudgetLine(
- inventoryCD: LaborInvCD,
- taskID: row.ProjectTaskID,
- accountGroupID: row.AccountGroupID,
- qty: pmHours,
- rate: PMRate,
- amount: pmHours * PMRate,
- uom: row.UOM,
- description: "Auto-added PM Labor"
- );
- }
- }
- finally
- {
- PXContext.SetSlot<bool?>(SlotLaborUpdate, false);
- }
- // ---- Popup & save -----------------------------------
- /*ShowPopupOnce(totalOrig);*/
- // Base.Actions.PressSave();
- }
- protected void _(Events.RowPersisting<PMRevenueBudget> e)
- {
- if (IsExcludedProject()) // ← add this line
- return;
- var row = (PMRevenueBudget)e.Row;
- if (row == null || e.Operation == PXDBOperation.Delete) return;
- if (Skip || PXContext.GetSlot<bool?>(SlotLaborUpdate) == true) return;
- if (LaborInvID != null && row.InventoryID == LaborInvID)
- {
- // ensure both currency and base rates are correct right before save
- decimal baseRate;
- PXCurrencyAttribute.CuryConvBase<PMRevenueBudget.curyUnitRate>(e.Cache, row, PMRate, out baseRate);
- if (row.CuryUnitRate != PMRate)
- e.Cache.SetValue<PMRevenueBudget.curyUnitRate>(row, PMRate);
- }
- }
- private bool IsExcludedProject()
- {
- PMProject proj = Base.Project.Current;
- if (proj == null) return false;
- // Branch check (BranchCD = 301) --------------------------------
- if (proj.DefaultBranchID != null)
- {
- Branch br = PXSelect<Branch,
- Where<Branch.branchID, Equal<Required<Branch.branchID>>>>
- .Select(Base, proj.DefaultBranchID);
- if (br?.BranchCD?.Trim() == ExcludedBranchCD)
- return true;
- }
- // Billing‑rule check (BillingRule = PROGRESS) ------------------
- // Field name differs by Acumatica build; common ones shown.
- string billRule = proj.BillingID; // e.g., 2021 R1+
- if (!string.IsNullOrEmpty(billRule) &&
- string.Equals(billRule.Trim(), ExcludedBillRule,
- StringComparison.OrdinalIgnoreCase))
- return true;
- return false;
- }
- public static bool IsActive()
- {
- return true;
- }
- protected void _(Events.FieldUpdated<PMRevenueBudget, PMRevenueBudget.curyUnitRate> e)
- {
- var r = (PMRevenueBudget)e.Row;
- if (r == null) return;
- PXTrace.WriteInformation($"curyUnitRate UPDATED -> {r.CuryUnitRate} (Inv={r.InventoryID})");
- }
- private PMRevenueBudget FindRevenueLineByInventoryCd(string inventoryCD)
- {
- if (string.IsNullOrEmpty(inventoryCD))
- return null;
- // Resolve InventoryID from the CD
- InventoryItem inv = InventoryItem.UK.Find(Base, inventoryCD);
- if (inv == null) return null;
- int? projID = Base.Project.Current?.ContractID;
- if (projID == null) return null;
- // 1) Cache (includes unsaved rows already on screen)
- var cached = Base.RevenueBudget.Cache.Cached
- .Cast<PMRevenueBudget>()
- .FirstOrDefault(r => r.ProjectID == projID
- && r.InventoryID == inv.InventoryID
- && r.Type == AccountType.Income);
- if (cached != null) return cached;
- // 2) DB (readonly, excludes unsaved edits)
- var db = PXSelectReadonly<PMRevenueBudget,
- Where<PMRevenueBudget.projectID, Equal<Required<PMRevenueBudget.projectID>>,
- And<PMRevenueBudget.inventoryID, Equal<Required<PMRevenueBudget.inventoryID>>,
- And<PMRevenueBudget.type, Equal<AccountType.income>>>>>
- .Select(Base, projID, inv.InventoryID)
- .RowCast<PMRevenueBudget>()
- .FirstOrDefault();
- return db;
- }
- public PMRevenueBudget CreateRevenueBudgetLine(
- int? inventoryID = null, string inventoryCD = null,
- int? taskID = null, string taskCD = null,
- int? accountGroupID = null, string accountGroupCD = null,
- decimal? qty = null,
- decimal? rate = null,
- decimal? amount = null, // if null, amount = qty * rate (when both provided)
- string uom = null,
- string description = null,
- int? costCodeID = null // optional
- )
- {
- // Resolve keys ----------------------------------------------------
- int? projectID = Base.Project.Current?.ContractID;
- if (projectID == null)
- throw new PXException();
- if (inventoryID == null && !string.IsNullOrEmpty(inventoryCD))
- inventoryID = InventoryItem.UK.Find(Base, inventoryCD)?.InventoryID;
- if (taskID == null && !string.IsNullOrEmpty(taskCD))
- taskID = PXSelect<PMTask,
- Where<PMTask.projectID, Equal<Required<PMTask.projectID>>,
- And<PMTask.taskCD, Equal<Required<PMTask.taskCD>>>>>
- .Select(Base, projectID, taskCD)
- ?.FirstOrDefault()?.GetItem<PMTask>()?.TaskID;
- if (accountGroupID == null && !string.IsNullOrEmpty(accountGroupCD))
- accountGroupID = PMAccountGroup.UK.Find(Base, accountGroupCD)?.GroupID;
- if (qty == null) qty = 0m;
- if (rate == null) rate = 0m;
- if (amount == null) amount = qty * rate;
- // Insert skeleton row --------------------------------------------
- var cache = Base.RevenueBudget.Cache;
- var line = new PMRevenueBudget
- {
- ProjectID = projectID,
- Type = AccountType.Income // revenue
- };
- line = (PMRevenueBudget)cache.Insert(line);
- // Set fields via SetValueExt so defaults/validations run ----------
- if (taskID != null)
- cache.SetValueExt<PMRevenueBudget.projectTaskID>(line, taskID);
- if (accountGroupID != null)
- cache.SetValueExt<PMRevenueBudget.accountGroupID>(line, accountGroupID);
- if (inventoryID != null)
- cache.SetValueExt<PMRevenueBudget.inventoryID>(line, inventoryID);
- if (costCodeID != null)
- cache.SetValueExt<PMRevenueBudget.costCodeID>(line, costCodeID);
- if (!string.IsNullOrEmpty(uom))
- cache.SetValueExt<PMRevenueBudget.uOM>(line, uom);
- if (qty != null)
- cache.SetValueExt<PMRevenueBudget.qty>(line, qty);
- if (rate != null)
- cache.SetValueExt<PMRevenueBudget.curyUnitRate>(line, rate);
- if (amount != null)
- cache.SetValueExt<PMRevenueBudget.curyAmount>(line, amount);
- if (!string.IsNullOrEmpty(description))
- cache.SetValueExt<PMRevenueBudget.description>(line, description);
- // Final update so cache/state is consistent
- line = (PMRevenueBudget)cache.Update(line);
- return line;
- }
- private decimal GetPMHours(decimal jobSize)
- {
- if (jobSize < 0m) return 0m; // or throw
- switch (jobSize)
- {
- case var n when n <= 615m: return 0.5m;
- case var n when n <= 1000m: return 1.0m;
- case var n when n <= 2500m: return 1.5m;
- case var n when n <= 5000m: return 3.0m;
- case var n when n <= 7500m: return 3.5m;
- case var n when n <= 10000m: return 5.0m;
- case var n when n <= 25000m: return 6.0m;
- case var n when n <= 50000m: return 7.0m;
- default: return 10.5m;
- }
- }
- private decimal GetRevisedRevenueTotalUI(params string[] excludeCDs)
- {
- // Resolve the IDs to skip
- var skip = new HashSet<int?>(
- (excludeCDs ?? new string[0])
- .Select(cd => InventoryItem.UK.Find(Base, cd)?.InventoryID)
- .Where(id => id != null)
- .Cast<int?>()
- );
- decimal total = 0m;
- // Select() returns rows currently in cache + remaining DB rows for the view
- foreach (PMRevenueBudget line in Base.RevenueBudget.Select().RowCast<PMRevenueBudget>())
- {
- if (line.Type == AccountType.Income && !skip.Contains(line.InventoryID))
- total += line.CuryRevisedAmount ?? 0m; // or CuryAmount if you want "original"
- }
- return total;
- }
- }
- }
|