Skip to content

Commit b63ddc8

Browse files
authored
[Bug][SubscriptionBilling]: Outdated ship-to address used on invoice when contract sell-to address is updated (#7538)
<!-- Thank you for submitting a Pull Request. If you're new to contributing to BCApps please read our pull request guideline below * https://github.com/microsoft/BCApps/Contributing.md --> #### Summary <!-- Provide a general summary of your changes --> When the sell-to address was changed on a Customer Subscription Contract with Ship-to Code blank (default), the ship-to address fields were not updated, causing invoices to use the outdated ship-to address. - Add UpdateShipToAddressFromSellToAddress to sync ship-to fields from sell-to on Customer Subscription Contract (aligned with Sales Header) - Add ShipToAddressEqualsSellToAddress and IsShipToAddressEqualToSellToAddress helper with integration event - Refactor UpdatePayToAddressFromBuyFromAddress on Vendor Subscription Contract with shared IsPayToAddressEqualToBuyFromAddress helper (aligned with Purchase Header) - Promote notification ID getters to internal for testability - Add tests for both customer and vendor contract address sync #### Work Item(s) <!-- Add the issue number here after the #. The issue needs to be open and approved. Submitting PRs with no linked issues or unapproved issues is highly discouraged. --> Fixes #6974 Fixes [AB#631934](https://dynamicssmb2.visualstudio.com/1fcb79e7-ab07-432a-a3c6-6cf5a88ba4a5/_workitems/edit/631934)
1 parent 6ba0f88 commit b63ddc8

4 files changed

Lines changed: 179 additions & 38 deletions

File tree

src/Apps/W1/Subscription Billing/App/Customer Contracts/Tables/CustomerSubscriptionContract.Table.al

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ table 8052 "Customer Subscription Contract"
430430

431431
trigger OnValidate()
432432
begin
433+
UpdateShipToAddressFromSellToAddress(FieldNo("Ship-to Address"));
433434
ModifyCustomerAddress();
434435
end;
435436
}
@@ -440,6 +441,7 @@ table 8052 "Customer Subscription Contract"
440441

441442
trigger OnValidate()
442443
begin
444+
UpdateShipToAddressFromSellToAddress(FieldNo("Ship-to Address 2"));
443445
ModifyCustomerAddress();
444446
end;
445447
}
@@ -468,6 +470,7 @@ table 8052 "Customer Subscription Contract"
468470
begin
469471
PostCode.ValidateCity(
470472
"Sell-to City", "Sell-to Post Code", "Sell-to County", "Sell-to Country/Region Code", (CurrFieldNo <> 0) and GuiAllowed);
473+
UpdateShipToAddressFromSellToAddress(FieldNo("Ship-to City"));
471474
ModifyCustomerAddress();
472475
end;
473476
}
@@ -492,6 +495,7 @@ table 8052 "Customer Subscription Contract"
492495

493496
trigger OnValidate()
494497
begin
498+
UpdateShipToAddressFromSellToAddress(FieldNo("Ship-to Contact"));
495499
ModifyCustomerAddress();
496500
end;
497501
}
@@ -576,6 +580,7 @@ table 8052 "Customer Subscription Contract"
576580

577581
PostCode.ValidatePostCode(
578582
"Sell-to City", "Sell-to Post Code", "Sell-to County", "Sell-to Country/Region Code", (CurrFieldNo <> 0) and GuiAllowed);
583+
UpdateShipToAddressFromSellToAddress(FieldNo("Ship-to Post Code"));
579584
ModifyCustomerAddress();
580585
end;
581586
}
@@ -587,6 +592,7 @@ table 8052 "Customer Subscription Contract"
587592

588593
trigger OnValidate()
589594
begin
595+
UpdateShipToAddressFromSellToAddress(FieldNo("Ship-to County"));
590596
ModifyCustomerAddress();
591597
end;
592598
}
@@ -598,6 +604,7 @@ table 8052 "Customer Subscription Contract"
598604

599605
trigger OnValidate()
600606
begin
607+
UpdateShipToAddressFromSellToAddress(FieldNo("Ship-to Country/Region Code"));
601608
ModifyCustomerAddress();
602609
end;
603610
}
@@ -1700,6 +1707,35 @@ table 8052 "Customer Subscription Contract"
17001707
exit(IsShipToAddressEqualToSellToAddress(xRec, Rec));
17011708
end;
17021709

1710+
internal procedure ShipToAddressEqualsSellToAddress(): Boolean
1711+
begin
1712+
exit(IsShipToAddressEqualToSellToAddress(Rec, Rec));
1713+
end;
1714+
1715+
local procedure UpdateShipToAddressFromSellToAddress(FieldNumber: Integer)
1716+
begin
1717+
if ("Ship-to Code" = '') and ShipToAddressEqualsOldSellToAddress() then
1718+
case FieldNumber of
1719+
FieldNo("Ship-to Address"):
1720+
"Ship-to Address" := "Sell-to Address";
1721+
FieldNo("Ship-to Address 2"):
1722+
"Ship-to Address 2" := "Sell-to Address 2";
1723+
FieldNo("Ship-to City"), FieldNo("Ship-to Post Code"):
1724+
begin
1725+
"Ship-to City" := "Sell-to City";
1726+
"Ship-to Post Code" := "Sell-to Post Code";
1727+
"Ship-to County" := "Sell-to County";
1728+
"Ship-to Country/Region Code" := "Sell-to Country/Region Code";
1729+
end;
1730+
FieldNo("Ship-to County"):
1731+
"Ship-to County" := "Sell-to County";
1732+
FieldNo("Ship-to Country/Region Code"):
1733+
"Ship-to Country/Region Code" := "Sell-to Country/Region Code";
1734+
FieldNo("Ship-to Contact"):
1735+
"Ship-to Contact" := "Sell-to Contact";
1736+
end;
1737+
end;
1738+
17031739
local procedure IsShipToAddressEqualToSellToAddress(CustomerContractWithSellTo: Record "Customer Subscription Contract"; CustomerContractWithShipTo: Record "Customer Subscription Contract"): Boolean
17041740
var
17051741
Result: Boolean;
@@ -1895,7 +1931,7 @@ table 8052 "Customer Subscription Contract"
18951931
ModifyCustomerAddressNotification.Recall();
18961932
end;
18971933

1898-
local procedure GetModifyCustomerAddressNotificationId(): Guid
1934+
internal procedure GetModifyCustomerAddressNotificationId(): Guid
18991935
begin
19001936
exit('D2EAE122-76DB-4D6D-B6ED-7A6EF9DC7F3D');
19011937
end;
@@ -2010,7 +2046,7 @@ table 8052 "Customer Subscription Contract"
20102046
end;
20112047

20122048
/// <summary>
2013-
/// Creates customer subscription contract line from subscription line which are not already assigned to a customer subscription contract line.
2049+
/// Creates customer subscription contract line from subscription line which are not already assigned to a customer subscription contract line.
20142050
/// </summary>
20152051
/// <param name="TempServiceCommitment">Temporary VAR Record "Subscription Line".</param>
20162052
procedure CreateCustomerContractLinesFromServiceCommitments(var TempServiceCommitment: Record "Subscription Line" temporary)

src/Apps/W1/Subscription Billing/App/Vendor Contracts/Tables/VendorSubscriptionContract.Table.al

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1426,12 +1426,12 @@ table 8063 "Vendor Subscription Contract"
14261426
ModifyVendorAddressNotification.Recall();
14271427
end;
14281428

1429-
local procedure GetModifyVendorAddressNotificationId(): Guid
1429+
internal procedure GetModifyVendorAddressNotificationId(): Guid
14301430
begin
14311431
exit('70D33B2A-0A18-44FB-9D27-2429FC5167ED');
14321432
end;
14331433

1434-
local procedure GetModifyPayToVendorAddressNotificationId(): Guid
1434+
internal procedure GetModifyPayToVendorAddressNotificationId(): Guid
14351435
begin
14361436
exit('CCEDACB9-211A-4457-919B-5B841759EBB5');
14371437
end;
@@ -1456,54 +1456,49 @@ table 8063 "Vendor Subscription Contract"
14561456
if PayToAddressEqualsOldBuyFromAddress() then
14571457
case FieldNumber of
14581458
FieldNo("Pay-to Address"):
1459-
if xRec."Buy-from Address" = "Pay-to Address" then
1460-
"Pay-to Address" := "Buy-from Address";
1459+
"Pay-to Address" := "Buy-from Address";
14611460
FieldNo("Pay-to Address 2"):
1462-
if xRec."Buy-from Address 2" = "Pay-to Address 2" then
1463-
"Pay-to Address 2" := "Buy-from Address 2";
1461+
"Pay-to Address 2" := "Buy-from Address 2";
14641462
FieldNo("Pay-to City"), FieldNo("Pay-to Post Code"):
14651463
begin
1466-
if xRec."Buy-from City" = "Pay-to City" then
1467-
"Pay-to City" := "Buy-from City";
1468-
if xRec."Buy-from Post Code" = "Pay-to Post Code" then
1469-
"Pay-to Post Code" := "Buy-from Post Code";
1470-
if xRec."Buy-from County" = "Pay-to County" then
1471-
"Pay-to County" := "Buy-from County";
1472-
if xRec."Buy-from Country/Region Code" = "Pay-to Country/Region Code" then
1473-
"Pay-to Country/Region Code" := "Buy-from Country/Region Code";
1464+
"Pay-to City" := "Buy-from City";
1465+
"Pay-to Post Code" := "Buy-from Post Code";
1466+
"Pay-to County" := "Buy-from County";
1467+
"Pay-to Country/Region Code" := "Buy-from Country/Region Code";
14741468
end;
14751469
FieldNo("Pay-to County"):
1476-
if xRec."Buy-from County" = "Pay-to County" then
1477-
"Pay-to County" := "Buy-from County";
1470+
"Pay-to County" := "Buy-from County";
14781471
FieldNo("Pay-to Country/Region Code"):
1479-
if xRec."Buy-from Country/Region Code" = "Pay-to Country/Region Code" then
1480-
"Pay-to Country/Region Code" := "Buy-from Country/Region Code";
1472+
"Pay-to Country/Region Code" := "Buy-from Country/Region Code";
14811473
end;
14821474
end;
14831475

14841476
local procedure PayToAddressEqualsOldBuyFromAddress(): Boolean
14851477
begin
1486-
if (xRec."Buy-from Address" = "Pay-to Address") and
1487-
(xRec."Buy-from Address 2" = "Pay-to Address 2") and
1488-
(xRec."Buy-from City" = "Pay-to City") and
1489-
(xRec."Buy-from County" = "Pay-to County") and
1490-
(xRec."Buy-from Post Code" = "Pay-to Post Code") and
1491-
(xRec."Buy-from Country/Region Code" = "Pay-to Country/Region Code")
1492-
then
1493-
exit(true);
1478+
exit(IsPayToAddressEqualToBuyFromAddress(xRec, Rec));
14941479
end;
14951480

14961481
internal procedure BuyFromAddressEqualsPayToAddress(): Boolean
14971482
begin
1498-
exit(
1499-
("Pay-to Address" = "Buy-from Address") and
1500-
("Pay-to Address 2" = "Buy-from Address 2") and
1501-
("Pay-to City" = "Buy-from City") and
1502-
("Pay-to County" = "Buy-from County") and
1503-
("Pay-to Post Code" = "Buy-from Post Code") and
1504-
("Pay-to Country/Region Code" = "Buy-from Country/Region Code") and
1505-
("Pay-to Contact No." = "Buy-from Contact No.") and
1506-
("Pay-to Contact" = "Buy-from Contact"));
1483+
exit(IsPayToAddressEqualToBuyFromAddress(Rec, Rec));
1484+
end;
1485+
1486+
local procedure IsPayToAddressEqualToBuyFromAddress(VendorContractWithBuyFrom: Record "Vendor Subscription Contract"; VendorContractWithPayTo: Record "Vendor Subscription Contract"): Boolean
1487+
var
1488+
Result: Boolean;
1489+
begin
1490+
Result :=
1491+
(VendorContractWithBuyFrom."Buy-from Address" = VendorContractWithPayTo."Pay-to Address") and
1492+
(VendorContractWithBuyFrom."Buy-from Address 2" = VendorContractWithPayTo."Pay-to Address 2") and
1493+
(VendorContractWithBuyFrom."Buy-from City" = VendorContractWithPayTo."Pay-to City") and
1494+
(VendorContractWithBuyFrom."Buy-from County" = VendorContractWithPayTo."Pay-to County") and
1495+
(VendorContractWithBuyFrom."Buy-from Post Code" = VendorContractWithPayTo."Pay-to Post Code") and
1496+
(VendorContractWithBuyFrom."Buy-from Country/Region Code" = VendorContractWithPayTo."Pay-to Country/Region Code") and
1497+
(VendorContractWithBuyFrom."Buy-from Contact No." = VendorContractWithPayTo."Pay-to Contact No.") and
1498+
(VendorContractWithBuyFrom."Buy-from Contact" = VendorContractWithPayTo."Pay-to Contact");
1499+
1500+
OnAfterIsPayToAddressEqualToBuyFromAddress(VendorContractWithBuyFrom, VendorContractWithPayTo, Result);
1501+
exit(Result);
15071502
end;
15081503

15091504
local procedure SetPurchaserCode(PurchaserCodeToCheck: Code[20]; var PurchaserCodeToAssign: Code[20])
@@ -1581,7 +1576,7 @@ table 8063 "Vendor Subscription Contract"
15811576
if VendorContractLine.FindSet() then
15821577
repeat
15831578
if not TempServiceObject.Get(VendorContractLine."Subscription Header No.") then begin
1584-
#pragma warning disable AA0214
1579+
#pragma warning disable AA0214
15851580
ServiceObject.Get(VendorContractLine."Subscription Header No.");
15861581
ServiceObject.UpdateServicesDates();
15871582
ServiceObject.Modify(false);
@@ -1807,4 +1802,9 @@ table 8063 "Vendor Subscription Contract"
18071802
local procedure OnAfterCreateDimDimSource(VendorSubscriptionContract: Record "Vendor Subscription Contract"; CurrFieldNo: Integer; var DefaultDimSource: List of [Dictionary of [Integer, Code[20]]])
18081803
begin
18091804
end;
1805+
1806+
[IntegrationEvent(false, false)]
1807+
local procedure OnAfterIsPayToAddressEqualToBuyFromAddress(VendorContractWithBuyFrom: Record "Vendor Subscription Contract"; VendorContractWithPayTo: Record "Vendor Subscription Contract"; var Result: Boolean)
1808+
begin
1809+
end;
18101810
}

src/Apps/W1/Subscription Billing/Test/Customer Contracts/ContractsTest.Codeunit.al

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2087,6 +2087,63 @@ codeunit 148155 "Contracts Test"
20872087
CustomerContract.TestField("Sell-to Customer No.", NewCustomer."No.");
20882088
end;
20892089

2090+
[Test]
2091+
procedure ShipToAddressIsUpdatedWhenSellToAddressIsChanged()
2092+
var
2093+
Customer: Record Customer;
2094+
CustomerContract: Record "Customer Subscription Contract";
2095+
begin
2096+
// [SCENARIO] Ship-To address fields are in sync with Sell-To address fields when Ship-to Code is blank
2097+
Initialize();
2098+
2099+
// [GIVEN] A customer contract where sell-to and ship-to addresses are equal
2100+
ContractTestLibrary.CreateCustomer(Customer);
2101+
ContractTestLibrary.CreateCustomerContract(CustomerContract, Customer."No.");
2102+
CustomerContract.DontNotifyCurrentUserAgain(CustomerContract.GetModifyBillToCustomerAddressNotificationId());
2103+
CustomerContract.DontNotifyCurrentUserAgain(CustomerContract.GetModifyCustomerAddressNotificationId());
2104+
2105+
Assert.AreEqual('', CustomerContract."Ship-to Code", 'Ship-to Code should be blank for default (sell-to) address.');
2106+
Assert.IsTrue(CustomerContract.ShipToAddressEqualsSellToAddress(), 'Setup: Ship-to and Sell-to address should be equal before the test.');
2107+
2108+
// [WHEN] Changing the sell-to address fields on the contract
2109+
CustomerContract.Validate("Sell-to Address", CopyStr(LibraryRandom.RandText(MaxStrLen(CustomerContract."Sell-to Address")), 1, MaxStrLen(CustomerContract."Sell-to Address")));
2110+
CustomerContract.Validate("Sell-to Address 2", CopyStr(LibraryRandom.RandText(MaxStrLen(CustomerContract."Sell-to Address 2")), 1, MaxStrLen(CustomerContract."Sell-to Address 2")));
2111+
CustomerContract.Validate("Sell-to Contact", CopyStr(LibraryRandom.RandText(MaxStrLen(CustomerContract."Sell-to Contact")), 1, MaxStrLen(CustomerContract."Sell-to Contact")));
2112+
CustomerContract.Modify(true);
2113+
2114+
// [THEN] Ship-To address fields are updated to match Sell-To address fields
2115+
Assert.IsTrue(CustomerContract.ShipToAddressEqualsSellToAddress(), 'Ship-to address fields should be in sync with Sell-to address fields.');
2116+
end;
2117+
2118+
[Test]
2119+
procedure ShipToAddressIsNotUpdatedWhenShipToCodeIsSet()
2120+
var
2121+
Customer: Record Customer;
2122+
CustomerContract: Record "Customer Subscription Contract";
2123+
ShipToAddress: Record "Ship-to Address";
2124+
begin
2125+
// [SCENARIO] Ship-To address fields are NOT updated when an alternate Ship-to Code is set
2126+
Initialize();
2127+
2128+
// [GIVEN] A customer contract with an alternate ship-to address
2129+
ContractTestLibrary.CreateCustomer(Customer);
2130+
ContractTestLibrary.CreateCustomerContract(CustomerContract, Customer."No.");
2131+
CustomerContract.DontNotifyCurrentUserAgain(CustomerContract.GetModifyBillToCustomerAddressNotificationId());
2132+
CustomerContract.DontNotifyCurrentUserAgain(CustomerContract.GetModifyCustomerAddressNotificationId());
2133+
2134+
ShipToAddress.SetRange("Customer No.", Customer."No.");
2135+
ShipToAddress.FindFirst();
2136+
CustomerContract.Validate("Ship-to Code", ShipToAddress.Code);
2137+
CustomerContract.Modify(true);
2138+
2139+
// [WHEN] Changing the sell-to address on the contract
2140+
CustomerContract.Validate("Sell-to Address", CopyStr(LibraryRandom.RandText(MaxStrLen(CustomerContract."Sell-to Address")), 1, MaxStrLen(CustomerContract."Sell-to Address")));
2141+
CustomerContract.Modify(true);
2142+
2143+
// [THEN] Ship-To address is NOT updated (it retains the alternate ship-to address)
2144+
Assert.AreNotEqual(CustomerContract."Sell-to Address", CustomerContract."Ship-to Address", 'Ship-to Address should not be changed when Ship-to Code is set.');
2145+
end;
2146+
20902147
#endregion Tests
20912148

20922149
#region Procedures

src/Apps/W1/Subscription Billing/Test/Vendor Contracts/VendorContractsTest.Codeunit.al

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,54 @@ codeunit 148154 "Vendor Contracts Test"
755755
VendorContract.TestField("Contract Type", ContractType.Code);
756756
end;
757757

758+
[Test]
759+
procedure PayToAddressIsUpdatedWhenBuyFromAddressIsChanged()
760+
begin
761+
// [SCENARIO] Pay-To address fields are in sync with Buy-From address fields when Pay-to Vendor No. equals Buy-from Vendor No.
762+
Initialize();
763+
764+
// [GIVEN] A vendor contract where buy-from and pay-to addresses are equal
765+
ContractTestLibrary.CreateVendor(Vendor);
766+
ContractTestLibrary.CreateVendorContract(VendorContract, Vendor."No.");
767+
VendorContract.DontNotifyCurrentUserAgain(VendorContract.GetModifyVendorAddressNotificationId());
768+
VendorContract.DontNotifyCurrentUserAgain(VendorContract.GetModifyPayToVendorAddressNotificationId());
769+
770+
AssertThat.IsTrue(VendorContract.BuyFromAddressEqualsPayToAddress(), 'Setup: Buy-from and Pay-to address should be equal before the test.');
771+
772+
// [WHEN] Changing the buy-from address fields on the contract
773+
VendorContract.Validate("Buy-from Address", CopyStr(LibraryRandom.RandText(MaxStrLen(VendorContract."Buy-from Address")), 1, MaxStrLen(VendorContract."Buy-from Address")));
774+
VendorContract.Validate("Buy-from Address 2", CopyStr(LibraryRandom.RandText(MaxStrLen(VendorContract."Buy-from Address 2")), 1, MaxStrLen(VendorContract."Buy-from Address 2")));
775+
VendorContract.Modify(true);
776+
777+
// [THEN] Pay-To address fields are updated to match Buy-From address fields
778+
AssertThat.IsTrue(VendorContract.BuyFromAddressEqualsPayToAddress(), 'Pay-to address fields should be in sync with Buy-from address fields.');
779+
end;
780+
781+
[Test]
782+
procedure PayToAddressIsNotUpdatedWhenPayToVendorIsDifferent()
783+
begin
784+
// [SCENARIO] Pay-To address fields are NOT updated when Pay-to Vendor differs from Buy-from Vendor
785+
Initialize();
786+
787+
// [GIVEN] A vendor contract with a different pay-to vendor
788+
ContractTestLibrary.CreateVendor(Vendor);
789+
ContractTestLibrary.CreateVendorInLCY(Vendor2);
790+
ContractTestLibrary.CreateVendorContract(VendorContract, Vendor."No.");
791+
VendorContract.DontNotifyCurrentUserAgain(VendorContract.GetModifyVendorAddressNotificationId());
792+
VendorContract.DontNotifyCurrentUserAgain(VendorContract.GetModifyPayToVendorAddressNotificationId());
793+
794+
VendorContract.SetHideValidationDialog(true);
795+
VendorContract.Validate("Pay-to Vendor No.", Vendor2."No.");
796+
VendorContract.Modify(true);
797+
798+
// [WHEN] Changing the buy-from address on the contract
799+
VendorContract.Validate("Buy-from Address", CopyStr(LibraryRandom.RandText(MaxStrLen(VendorContract."Buy-from Address")), 1, MaxStrLen(VendorContract."Buy-from Address")));
800+
VendorContract.Modify(true);
801+
802+
// [THEN] Pay-To address is NOT updated (it retains the alternate pay-to vendor address)
803+
AssertThat.AreNotEqual(VendorContract."Buy-from Address", VendorContract."Pay-to Address", 'Pay-to Address should not be changed when Pay-to Vendor differs from Buy-from Vendor.');
804+
end;
805+
758806
#endregion Tests
759807

760808
#region Procedures

0 commit comments

Comments
 (0)