Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ codeunit 20516 "Subc. Req. Wksh. Make Ord."
[EventSubscriber(ObjectType::Codeunit, Codeunit::"Req. Wksh.-Make Order", OnInsertPurchOrderLineOnAfterTransferFromReqLineToPurchLine, '', false, false)]
local procedure OnInsertPurchOrderLineOnAfterTransferFromReqLineToPurchLine(var PurchOrderLine: Record "Purchase Line"; RequisitionLine: Record "Requisition Line")
var
ProdOrderRoutingLine: Record "Prod. Order Routing Line";
SubcPriceManagement: Codeunit "Subc. Price Management";
AutomaticReqLineCost: Decimal;
begin
#if not CLEAN29
#pragma warning disable AL0432
Expand All @@ -43,7 +45,11 @@ codeunit 20516 "Subc. Req. Wksh. Make Ord."
if (RequisitionLine."Prod. Order No." = '') or (RequisitionLine."Operation No." = '') then
exit;

SubcPriceManagement.GetSubcPriceForPurchLine(PurchOrderLine);
AutomaticReqLineCost := SubcPriceManagement.GetAutomaticSubcCostForReqLine(RequisitionLine, ProdOrderRoutingLine);
if RequisitionLine."Direct Unit Cost" <> AutomaticReqLineCost then
exit;

SubcPriceManagement.GetSubcPriceForPurchLine(PurchOrderLine, ProdOrderRoutingLine);
end;

[EventSubscriber(ObjectType::Codeunit, Codeunit::"Req. Wksh.-Make Order", OnInsertPurchOrderLineOnAfterCheckInsertFinalizePurchaseOrderHeader, '', false, false)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,16 +365,18 @@ codeunit 20508 "Subc. Price Management"
PriceListQty := QtyBase / PriceListQtyPerUOM;
end;

local procedure GetPriceByUOM(var SubcontractorPrice: Record "Subcontractor Price"; PriceListQty: Decimal; var PriceListCost: Decimal)
local procedure GetPriceByUOM(var SubcontractorPrice: Record "Subcontractor Price"; PriceListQty: Decimal; var PriceListCost: Decimal): Boolean
begin
SubcontractorPrice.SetRange("Minimum Quantity", 0, PriceListQty);
SubcontractorPrice.SetRange("Unit of Measure Code", SubcontractorPrice."Unit of Measure Code");
if SubcontractorPrice.FindLast() then begin
PriceListCost := SubcontractorPrice."Direct Unit Cost";
if PriceListCost <> 0 then
if (PriceListCost * PriceListQty) < SubcontractorPrice."Minimum Amount" then
PriceListCost := SubcontractorPrice."Minimum Amount" / PriceListQty;
end;
if not SubcontractorPrice.FindLast() then
exit(false);

PriceListCost := SubcontractorPrice."Direct Unit Cost";
if (PriceListCost <> 0) and (PriceListQty <> 0) then
if (PriceListCost * PriceListQty) < SubcontractorPrice."Minimum Amount" then
PriceListCost := SubcontractorPrice."Minimum Amount" / PriceListQty;
Comment thread
ChethanT marked this conversation as resolved.
exit(true);
end;

procedure ConvertPriceToUOM(ProdUOM: Code[10]; ProdQtyPerUoM: Decimal; PriceListUOM: Code[10]; PriceListQtyPerUOM: Decimal; PriceListCost: Decimal; var DirectCost: Decimal)
Expand Down Expand Up @@ -483,7 +485,13 @@ codeunit 20508 "Subc. Price Management"
end else
GetUOMPrice(RequisitionLine."No.", RequisitionLine.GetQuantityBase(), SubcontractorPrice, PriceListUOM, PriceListQtyPerUOM, PriceListQty);

GetPriceByUOM(SubcontractorPrice, PriceListQty, PriceListCost);
if not GetPriceByUOM(SubcontractorPrice, PriceListQty, PriceListCost) then begin
RequisitionLine."Subc. Pricelist Cost" := 0;
RequisitionLine."Subc. UoM for Pricelist" := '';
RequisitionLine."Base UM Qty/PL UM Qty" := 1;
RequisitionLine."PL UM Qty/Base UM Qty" := 1;
exit;
end;
if PriceListCost <> 0 then begin
ConvertPriceToUOM(RequisitionLine."Unit of Measure Code", RequisitionLine.GetQuantityForUOM(), PriceListUOM, PriceListQtyPerUOM, PriceListCost, DirectCost);
ConvertPriceToCurrency(RequisitionLine."Currency Code", SubcontractorPrice."Currency Code", PriceListCost, DirectCost);
Expand All @@ -503,13 +511,40 @@ codeunit 20508 "Subc. Price Management"
end;
end;

internal procedure GetAutomaticSubcCostForReqLine(RequisitionLine: Record "Requisition Line"): Decimal
var
ProdOrderRoutingLine: Record "Prod. Order Routing Line";
begin
exit(GetAutomaticSubcCostForReqLine(RequisitionLine, ProdOrderRoutingLine));
end;

internal procedure GetAutomaticSubcCostForReqLine(RequisitionLine: Record "Requisition Line"; var ProdOrderRoutingLine: Record "Prod. Order Routing Line"): Decimal
begin
ProdOrderRoutingLine.SetLoadFields(
"Standard Task Code", Type, "Unit Cost Calculation", "Direct Unit Cost",
"Expected Operation Cost Amt.", "Expected Capacity Ovhd. Cost");
GetProdOrderRtngLine(
RequisitionLine."Prod. Order No.", RequisitionLine."Routing Reference No.",
RequisitionLine."Routing No.", RequisitionLine."Operation No.", ProdOrderRoutingLine);
ProdOrderRoutingLine.TestField(Type, "Capacity Type"::"Work Center");
RequisitionLine."Direct Unit Cost" := GetNonPriceListDirectCost(ProdOrderRoutingLine);
GetSubcPriceForReqLine(RequisitionLine, '');
exit(RequisitionLine."Direct Unit Cost");
end;

procedure GetSubcPriceForPurchLine(var PurchaseLine: Record "Purchase Line")
var
ProdOrderRoutingLine: Record "Prod. Order Routing Line";
SubcontractorPrice: Record "Subcontractor Price";
PriceListUOM: Code[10];
begin
GetSubcPriceForPurchLine(PurchaseLine, ProdOrderRoutingLine);
end;

// ProdOrderRoutingLine can be pre-populated by the caller (e.g. from a prior GetAutomaticSubcCostForReqLine call
// for the same operation) to avoid re-reading the same Prod. Order Routing Line record.
procedure GetSubcPriceForPurchLine(var PurchaseLine: Record "Purchase Line"; var ProdOrderRoutingLine: Record "Prod. Order Routing Line")
var
OrderDate: Date;
DirectCost, PriceListCost, PriceListQty, PriceListQtyPerUOM : Decimal;
DirectCost: Decimal;
begin
#if not CLEAN29
#pragma warning disable AL0432
Expand All @@ -521,36 +556,89 @@ codeunit 20508 "Subc. Price Management"
if OrderDate = 0D then
OrderDate := WorkDate();

if not TryGetSubcPriceListCostForPurchLine(PurchaseLine, OrderDate, DirectCost, ProdOrderRoutingLine) then begin
ProdOrderRoutingLine.TestField(Type, "Capacity Type"::"Work Center");
DirectCost := GetNonPriceListDirectCost(ProdOrderRoutingLine);
end;

PurchaseLine."Direct Unit Cost" := DirectCost;
PurchaseLine.Validate("Line Discount %");
end;

local procedure TryGetSubcPriceListCostForPurchLine(PurchaseLine: Record "Purchase Line"; OrderDate: Date; var DirectCost: Decimal; var ProdOrderRoutingLine: Record "Prod. Order Routing Line"): Boolean
var
SubcontractorPrice: Record "Subcontractor Price";
PriceListUOM: Code[10];
PriceListCost, PriceListQty, PriceListQtyPerUOM : Decimal;
begin
DirectCost := 0;
#if not CLEAN29
#pragma warning disable AL0432
if not SubcFeatureFlagHandler.IsSubcontractingEnabled() then
#pragma warning restore AL0432
exit(false);
#endif
if OrderDate = 0D then
OrderDate := WorkDate();

if not IsProdOrderRoutingLineForPurchLine(ProdOrderRoutingLine, PurchaseLine) then begin
ProdOrderRoutingLine.SetLoadFields(
"Standard Task Code", Type, "Unit Cost Calculation", "Direct Unit Cost",
"Expected Operation Cost Amt.", "Expected Capacity Ovhd. Cost");
GetProdOrderRtngLine(
PurchaseLine."Prod. Order No.", PurchaseLine."Routing Reference No.",
PurchaseLine."Routing No.", PurchaseLine."Operation No.", ProdOrderRoutingLine);
end;

SubcontractorPrice.SetRange("Vendor No.", PurchaseLine."Buy-from Vendor No.");
SubcontractorPrice.SetRange("Work Center No.", PurchaseLine."Work Center No.");
SubcontractorPrice.SetRange("Item No.", PurchaseLine."No.");
SubcontractorPrice.SetFilter("Variant Code", '%1|%2', PurchaseLine."Variant Code", '');
SubcontractorPrice.SetFilter("Unit of Measure Code", '%1|%2', PurchaseLine."Unit of Measure Code", '');

GetProdOrderRtngLine(PurchaseLine."Prod. Order No.", PurchaseLine."Routing Reference No.", PurchaseLine."Routing No.", PurchaseLine."Operation No.", ProdOrderRoutingLine);

SubcontractorPrice.SetFilter("Standard Task Code", '%1|%2', ProdOrderRoutingLine."Standard Task Code", '');
SubcontractorPrice.SetFilter("Currency Code", '%1|%2', PurchaseLine."Currency Code", '');
SubcontractorPrice.SetRange("Starting Date", 0D, OrderDate);
SubcontractorPrice.SetFilter("Ending Date", '>=%1|%2', OrderDate, 0D);

if SubcontractorPrice.FindLast() then begin
if SubcontractorPrice."Unit of Measure Code" = PurchaseLine."Unit of Measure Code" then
PriceListUOM := SubcontractorPrice."Unit of Measure Code";
GetUOMPrice(PurchaseLine."No.", GetQuantityBase(PurchaseLine), SubcontractorPrice, PriceListUOM, PriceListQtyPerUOM, PriceListQty);
GetPriceByUOM(SubcontractorPrice, PriceListQty, PriceListCost);
if PriceListCost <> 0 then begin
ConvertPriceToUOM(PurchaseLine."Unit of Measure Code", PurchaseLine.GetQuantityPerUOM(), PriceListUOM, PriceListQtyPerUOM, PriceListCost, DirectCost);
ConvertPriceToCurrency(PurchaseLine."Currency Code", SubcontractorPrice."Currency Code", PriceListCost, DirectCost)
end;
end else begin
GetUOMPrice(PurchaseLine."No.", PurchaseLine.GetQuantityBase(), SubcontractorPrice, PriceListUOM, PriceListQtyPerUOM, PriceListQty);
ProdOrderRoutingLine.TestField(Type, "Capacity Type"::"Work Center");
DirectCost := ProdOrderRoutingLine."Direct Unit Cost";
end;
if not SubcontractorPrice.FindLast() then
exit(false);

PurchaseLine."Direct Unit Cost" := DirectCost;
PurchaseLine.Validate("Line Discount %");
if SubcontractorPrice."Unit of Measure Code" = PurchaseLine."Unit of Measure Code" then
PriceListUOM := SubcontractorPrice."Unit of Measure Code";
GetUOMPrice(PurchaseLine."No.", GetQuantityBase(PurchaseLine), SubcontractorPrice, PriceListUOM, PriceListQtyPerUOM, PriceListQty);
if not GetPriceByUOM(SubcontractorPrice, PriceListQty, PriceListCost) then
exit(false);
if PriceListCost = 0 then
exit(true);

ConvertPriceToUOM(PurchaseLine."Unit of Measure Code", PurchaseLine.GetQuantityPerUOM(), PriceListUOM, PriceListQtyPerUOM, PriceListCost, DirectCost);
ConvertPriceToCurrency(PurchaseLine."Currency Code", SubcontractorPrice."Currency Code", PriceListCost, DirectCost);
exit(true);
end;

local procedure GetNonPriceListDirectCost(ProdOrderRoutingLine: Record "Prod. Order Routing Line"): Decimal
var
GeneralLedgerSetup: Record "General Ledger Setup";
ProdOrderLine: Record "Prod. Order Line";
begin
ProdOrderLine.SetLoadFields("Qty. per Unit of Measure");
GetLine(ProdOrderLine, ProdOrderRoutingLine);
Comment thread
ChethanT marked this conversation as resolved.
GeneralLedgerSetup.Get();
if ProdOrderRoutingLine."Unit Cost Calculation" = ProdOrderRoutingLine."Unit Cost Calculation"::Units then
exit(
Round(
ProdOrderRoutingLine."Direct Unit Cost" * ProdOrderLine."Qty. per Unit of Measure",
GeneralLedgerSetup."Unit-Amount Rounding Precision"));

ProdOrderLine.CalcFields("Total Exp. Oper. Output (Qty.)");
Comment thread
ChethanT marked this conversation as resolved.
if ProdOrderLine."Total Exp. Oper. Output (Qty.)" = 0 then
exit(0);

exit(
Round(
(ProdOrderRoutingLine."Expected Operation Cost Amt." - ProdOrderRoutingLine."Expected Capacity Ovhd. Cost") /
ProdOrderLine."Total Exp. Oper. Output (Qty.)",
GeneralLedgerSetup."Unit-Amount Rounding Precision"));
end;

local procedure GetProdOrderRtngLine(ProdOrderNo: Code[20]; RtngRefNo: Integer; RoutingNo: Code[20]; OperationNo: Code[10]; var ProdOrderRoutingLine: Record "Prod. Order Routing Line")
Expand All @@ -564,6 +652,16 @@ codeunit 20508 "Subc. Price Management"
ProdOrderRoutingLine.FindFirst();
end;

local procedure IsProdOrderRoutingLineForPurchLine(ProdOrderRoutingLine: Record "Prod. Order Routing Line"; PurchaseLine: Record "Purchase Line"): Boolean
begin
exit(
(ProdOrderRoutingLine."Prod. Order No." <> '') and
(ProdOrderRoutingLine."Prod. Order No." = PurchaseLine."Prod. Order No.") and
(ProdOrderRoutingLine."Routing Reference No." = PurchaseLine."Routing Reference No.") and
(ProdOrderRoutingLine."Routing No." = PurchaseLine."Routing No.") and
(ProdOrderRoutingLine."Operation No." = PurchaseLine."Operation No."));
end;

local procedure SetSubcontractorPriceForPriceCalculation(var SubcontractorPrice: Record "Subcontractor Price"; VendorNo: Code[20]; ItemNo: Code[20]; VariantCode: Code[10]; StandardTaskCode: Code[10]; WorkCenterNo: Code[20]; UoM: Code[10]; StartingDate: Date)
var
Vendor: Record Vendor;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,14 @@ codeunit 20534 "Subc. Purchase Line Ext"
if GetExecutionContext() = ExecutionContext::Upgrade then
exit;

if Rec."Planned Receipt Date" = xRec."Planned Receipt Date" then
// Gate on the resulting Order Date rather than Planned Receipt Date so lead-time-only
// reschedules (for example changing Lead Time Calculation on an open line with nonblank
// Requested and Planned Receipt Dates) still trigger date-effective repricing when the
// planned-date validation reassigns Order Date without changing Planned Receipt Date.
if Rec."Order Date" = xRec."Order Date" then
exit;

GetSubcontractingPrice(Rec);
RepriceSubcPurchLineOnScheduleChange(Rec);
end;

[EventSubscriber(ObjectType::Table, Database::"Purchase Line", OnAfterValidateEvent, "Order Date", false, false)]
Expand All @@ -124,7 +128,7 @@ codeunit 20534 "Subc. Purchase Line Ext"
if Rec."Order Date" = xRec."Order Date" then
exit;

GetSubcontractingPrice(Rec);
RepriceSubcPurchLineOnScheduleChange(Rec);
end;

[EventSubscriber(ObjectType::Table, Database::"Purchase Line", OnAfterValidateEvent, Quantity, false, false)]
Expand Down Expand Up @@ -354,6 +358,32 @@ codeunit 20534 "Subc. Purchase Line Ext"
SubcPriceManagement.GetSubcPriceForPurchLine(PurchaseLine);
end;

local procedure RepriceSubcPurchLineOnScheduleChange(var PurchaseLine: Record "Purchase Line")
var
PurchaseHeader: Record "Purchase Header";
begin
if (PurchaseLine.Type <> PurchaseLine.Type::Item) or (PurchaseLine."No." = '') or
(PurchaseLine."Prod. Order No." = '') or (PurchaseLine."Operation No." = '')
then
exit;

// Preserve released-order scheduling: repricing a subcontracting line after release
// would call Validate("Line Discount %") through GetSubcPriceForPurchLine, which in
// turn calls TestStatusOpen on the released header and fails the date edit. The base
// test suite explicitly permits Planned Receipt Date and Order Date edits on released
// purchase order lines (see ERMSalesPurchStatusError CanChangeOrderDateOnReleasedPurchOrderLine
// and CanChangePlannedReceiptDateOnReleasedPurchOrderLine). Bypass repricing entirely
// once the header is no longer Open so scheduling still works without silently
// changing released financial terms.
PurchaseHeader.SetLoadFields(Status);
Comment thread
ChethanT marked this conversation as resolved.
if not PurchaseHeader.Get(PurchaseLine."Document Type", PurchaseLine."Document No.") then
exit;
if PurchaseHeader.Status <> PurchaseHeader.Status::Open then
exit;

GetSubcontractingPrice(PurchaseLine);
end;

[EventSubscriber(ObjectType::Table, Database::"Purchase Line", OnBeforeOpenItemTrackingLines, '', false, false)]
local procedure OpenProdOrderLineItemTrackingOnBeforeOpenItemTrackingLines(PurchaseLine: Record "Purchase Line"; var IsHandled: Boolean)
begin
Expand Down
Loading
Loading