diff --git a/Base/Database/Domain.Tests/Order/SalesOrderTests.cs b/Base/Database/Domain.Tests/Order/SalesOrderTests.cs index aa55ff6655..a500cefa68 100644 --- a/Base/Database/Domain.Tests/Order/SalesOrderTests.cs +++ b/Base/Database/Domain.Tests/Order/SalesOrderTests.cs @@ -2752,6 +2752,7 @@ public void GivenSalesOrder_WhenObjectStateIsProvisional_ThenCheckTransitions() Assert.True(acl.CanExecute(M.SalesOrder.Hold)); Assert.False(acl.CanExecute(M.SalesOrder.Continue)); Assert.False(acl.CanExecute(M.SalesOrder.Accept)); + Assert.True(acl.CanExecute(M.SalesOrder.DoTransfer)); } [Fact] diff --git a/Base/Database/Domain.Tests/Order/SalesOrderTransferTests.cs b/Base/Database/Domain.Tests/Order/SalesOrderTransferTests.cs new file mode 100644 index 0000000000..56d28a72ca --- /dev/null +++ b/Base/Database/Domain.Tests/Order/SalesOrderTransferTests.cs @@ -0,0 +1,3028 @@ +// +// Copyright (c) Allors bvba. All rights reserved. +// Licensed under the LGPL license. See LICENSE file in the project root for full license information. +// +// Defines the MediaTests type. + +namespace Allors.Domain +{ + using System; + using System.Linq; + using Allors.Domain.TestPopulation; + using Allors.Meta; + + using Xunit; + + public class SalesOrderTransferTests : DomainTest + { + [Fact] + public void GivenSalesOrderBuilder_WhenProvisional_ThenOrderCanTranseft() + { + var customer = new PersonBuilder(this.Session).WithFirstName("Koen").Build(); + var internalOrganisation = this.InternalOrganisation; + + var anotherInternalOrganisation = new OrganisationBuilder(this.Session) + .WithIsInternalOrganisation(true) + .WithDoAccounting(false) + .WithName("another internalOrganisation") + .WithPreferredCurrency(new Currencies(this.Session).CurrencyByCode["EUR"]) + .WithIncomingShipmentNumberPrefix("incoming shipmentno: ") + .WithPurchaseInvoiceNumberPrefix("incoming invoiceno: ") + .WithPurchaseOrderNumberPrefix("purchase orderno: ") + .WithSubAccountCounter(new CounterBuilder(this.Session).WithUniqueId(Guid.NewGuid()).WithValue(0).Build()) + .Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(internalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + this.Session.Derive(); + + var transfer = new SalesOrderTransferBuilder(this.Session) + .WithFrom(order) + .WithInternalOrganisation(anotherInternalOrganisation) + .Build(); + + this.Session.Derive(); + + Assert.True(transfer.ExistTo); + Assert.Equal(transfer.To.TakenBy, anotherInternalOrganisation); + Assert.Equal(order.ShipToCustomer, transfer.To.ShipToCustomer); + //TODO: Add More Asserts + + } + + [Fact] + public void GivenSalesOrderForItemsThatAreAvailable_WhenShipped_ThenOrderIsCompleted() + { + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var mechelenAddress = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build(); + var shipToMechelen = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(mechelenAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var billToMechelen = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(mechelenAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).BillingAddress) + .WithUseAsDefault(true) + .Build(); + + var good1 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + var good2 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good2"); + + var customer = new PersonBuilder(this.Session).WithLastName("customer").WithPartyContactMechanism(shipToMechelen).WithPartyContactMechanism(billToMechelen).Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + this.Session.Derive(); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithPart(good1.Part).WithReason(new InventoryTransactionReasons(this.Session).Unknown).Build(); + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithPart(good2.Part).WithReason(new InventoryTransactionReasons(this.Session).Unknown).Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(mechelenAddress) + .Build(); + + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(1).WithAssignedUnitPrice(15).Build(); + var item2 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(2).WithAssignedUnitPrice(15).Build(); + var item3 = new SalesOrderItemBuilder(this.Session).WithProduct(good2).WithQuantityOrdered(5).WithAssignedUnitPrice(15).Build(); + order.AddSalesOrderItem(item1); + order.AddSalesOrderItem(item2); + order.AddSalesOrderItem(item3); + + this.Session.Derive(); + + order.SetReadyForPosting(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + var shipment = (CustomerShipment)mechelenAddress.ShipmentsWhereShipToAddress[0]; + + shipment.Pick(); + this.Session.Derive(); + + var pickList = shipment.ShipmentItems[0].ItemIssuancesWhereShipmentItem[0].PickListItem.PickListWherePickListItem; + pickList.Picker = this.OrderProcessor; + + // var derivation = new Allors.Domain.Logging.Derivation(this.Session, new DerivationConfig { DerivationLogFunc = () => new DerivationLog() }); + // derivation.Derive(); + + // var list = ((DerivationLog)derivation.DerivationLog).List; + ////list.RemoveAll(v => !v.StartsWith("Dependency")); + + pickList.SetPicked(); + this.Session.Derive(); + + var package = new ShipmentPackageBuilder(this.Session).Build(); + shipment.AddShipmentPackage(package); + + foreach (ShipmentItem shipmentItem in shipment.ShipmentItems) + { + package.AddPackagingContent(new PackagingContentBuilder(this.Session).WithShipmentItem(shipmentItem).WithQuantity(shipmentItem.Quantity).Build()); + } + + this.Session.Derive(); + + shipment.Ship(); + + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).Completed, order.SalesOrderState); + Assert.Equal(new SalesOrderItemStates(this.Session).Completed, item1.SalesOrderItemState); + Assert.Equal(new SalesOrderItemStates(this.Session).Completed, item2.SalesOrderItemState); + Assert.Equal(new SalesOrderItemStates(this.Session).Completed, item3.SalesOrderItemState); + } + + [Fact] + public void GivenSalesOrderShippedInMultipleParts_WhenPaymentsAreReceived_ThenObjectStateCorrespondingSalesOrderIsUpdated() + { + var assessable = new VatRegimes(this.Session).Assessable21; + var vatRate0 = new VatRateBuilder(this.Session).WithRate(0).Build(); + assessable.VatRate = vatRate0; + + var good1 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + var good2 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good2"); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(1).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good1.Part).Build(); + + this.Session.Derive(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var mechelenAddress = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build(); + var shipToMechelen = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(mechelenAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var billToMechelen = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(mechelenAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).BillingAddress) + .WithUseAsDefault(true) + .Build(); + + var customer = new PersonBuilder(this.Session).WithLastName("customer").WithPartyContactMechanism(shipToMechelen).WithPartyContactMechanism(billToMechelen).Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithVatRegime(assessable) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(1).WithAssignedUnitPrice(15).WithComment("item1").Build(); + var item2 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(2).WithAssignedUnitPrice(15).WithComment("item2").Build(); + var item3 = new SalesOrderItemBuilder(this.Session).WithProduct(good2).WithQuantityOrdered(5).WithAssignedUnitPrice(15).WithComment("item3").Build(); + order.AddSalesOrderItem(item1); + order.AddSalesOrderItem(item2); + order.AddSalesOrderItem(item3); + + this.Session.Derive(); + + order.SetReadyForPosting(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + var shipment = (CustomerShipment)item1.OrderShipmentsWhereOrderItem[0].ShipmentItem.ShipmentWhereShipmentItem; + + shipment.Pick(); + this.Session.Derive(); + + var pickList = shipment.ShipmentItems[0].ItemIssuancesWhereShipmentItem[0].PickListItem.PickListWherePickListItem; + pickList.Picker = this.OrderProcessor; + + pickList.SetPicked(); + this.Session.Derive(); + + var package = new ShipmentPackageBuilder(this.Session).Build(); + shipment.AddShipmentPackage(package); + + foreach (ShipmentItem shipmentItem in shipment.ShipmentItems) + { + package.AddPackagingContent(new PackagingContentBuilder(this.Session).WithShipmentItem(shipmentItem).WithQuantity(shipmentItem.Quantity).Build()); + } + + this.Session.Derive(); + + shipment.SetPacked(); + this.Session.Derive(); + + shipment.Ship(); + this.Session.Derive(); + + var salesInvoiceitem = (SalesInvoiceItem)shipment.ShipmentItems[0].ShipmentItemBillingsWhereShipmentItem[0].InvoiceItem; + var invoice1 = (SalesInvoice)salesInvoiceitem.SalesInvoiceWhereSalesInvoiceItem; + invoice1.Send(); + + new ReceiptBuilder(this.Session) + .WithAmount(15) + .WithPaymentApplication(new PaymentApplicationBuilder(this.Session).WithInvoiceItem(invoice1.SalesInvoiceItems[0]).WithAmountApplied(15).Build()) + .WithEffectiveDate(this.Session.Now()) + .Build(); + + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).InProcess, order.SalesOrderState); + Assert.Equal(new SalesOrderItemStates(this.Session).Finished, item1.SalesOrderItemState); + Assert.Equal(new SalesOrderItemStates(this.Session).InProcess, item2.SalesOrderItemState); + Assert.Equal(new SalesOrderItemStates(this.Session).InProcess, item3.SalesOrderItemState); + + Assert.Equal(1, item1.QuantityShipped); + Assert.Equal(0, item1.QuantityCommittedOut); + Assert.Equal(0, item1.QuantityPendingShipment); + Assert.Equal(0, item1.QuantityRequestsShipping); + Assert.Equal(0, item1.QuantityShortFalled); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good1.Part).Build(); + + this.Session.Derive(); + + shipment = (CustomerShipment)item2.OrderShipmentsWhereOrderItem[0].ShipmentItem.ShipmentWhereShipmentItem; + + shipment.Pick(); + this.Session.Derive(); + + pickList = shipment.ShipmentItems[0].ItemIssuancesWhereShipmentItem[0].PickListItem.PickListWherePickListItem; + pickList.Picker = this.OrderProcessor; + + pickList.SetPicked(); + + this.Session.Derive(); + + package = new ShipmentPackageBuilder(this.Session).Build(); + shipment.AddShipmentPackage(package); + + foreach (ShipmentItem shipmentItem in shipment.ShipmentItems) + { + package.AddPackagingContent(new PackagingContentBuilder(this.Session).WithShipmentItem(shipmentItem).WithQuantity(shipmentItem.Quantity).Build()); + } + + this.Session.Derive(); + + shipment.Ship(); + + this.Session.Derive(); + + salesInvoiceitem = (SalesInvoiceItem)shipment.ShipmentItems[0].ShipmentItemBillingsWhereShipmentItem[0].InvoiceItem; + var invoice2 = (SalesInvoice)salesInvoiceitem.SalesInvoiceWhereSalesInvoiceItem; + invoice2.Send(); + + new ReceiptBuilder(this.Session) + .WithAmount(30) + .WithPaymentApplication(new PaymentApplicationBuilder(this.Session).WithInvoiceItem(invoice2.SalesInvoiceItems[0]).WithAmountApplied(30).Build()) + .Build(); + + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).InProcess, order.SalesOrderState); + Assert.Equal(new SalesOrderItemStates(this.Session).Finished, item1.SalesOrderItemState); + Assert.Equal(new SalesOrderItemStates(this.Session).Finished, item2.SalesOrderItemState); + Assert.Equal(new SalesOrderItemStates(this.Session).InProcess, item3.SalesOrderItemState); + + Assert.Equal(1, item1.QuantityShipped); + Assert.Equal(0, item1.QuantityCommittedOut); + Assert.Equal(0, item1.QuantityPendingShipment); + Assert.Equal(0, item1.QuantityRequestsShipping); + Assert.Equal(0, item1.QuantityShortFalled); + + Assert.Equal(2, item2.QuantityShipped); + Assert.Equal(0, item2.QuantityCommittedOut); + Assert.Equal(0, item2.QuantityPendingShipment); + Assert.Equal(0, item2.QuantityRequestsShipping); + Assert.Equal(0, item2.QuantityShortFalled); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good2.Part).Build(); + + this.Session.Derive(); + + shipment = (CustomerShipment)item3.OrderShipmentsWhereOrderItem[0].ShipmentItem.ShipmentWhereShipmentItem; + + shipment.Pick(); + this.Session.Derive(); + + pickList = shipment.ShipmentItems[0].ItemIssuancesWhereShipmentItem[0].PickListItem.PickListWherePickListItem; + pickList.Picker = this.OrderProcessor; + + pickList.SetPicked(); + + this.Session.Derive(); + + package = new ShipmentPackageBuilder(this.Session).Build(); + shipment.AddShipmentPackage(package); + + foreach (ShipmentItem shipmentItem in shipment.ShipmentItems) + { + package.AddPackagingContent(new PackagingContentBuilder(this.Session).WithShipmentItem(shipmentItem).WithQuantity(shipmentItem.Quantity).Build()); + } + + this.Session.Derive(); + + shipment.Ship(); + + this.Session.Derive(); + + salesInvoiceitem = + (SalesInvoiceItem)shipment.ShipmentItems[0].ShipmentItemBillingsWhereShipmentItem[0].InvoiceItem; + var invoice3 = salesInvoiceitem.SalesInvoiceWhereSalesInvoiceItem; + + new ReceiptBuilder(this.Session) + .WithAmount(75) + .WithPaymentApplication(new PaymentApplicationBuilder(this.Session).WithInvoiceItem(invoice3.SalesInvoiceItems[0]).WithAmountApplied(75).Build()) + .Build(); + + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).Finished, order.SalesOrderState); + Assert.Equal(new SalesOrderItemStates(this.Session).Finished, item1.SalesOrderItemState); + Assert.Equal(new SalesOrderItemStates(this.Session).Finished, item2.SalesOrderItemState); + Assert.Equal(new SalesOrderItemStates(this.Session).Finished, item3.SalesOrderItemState); + } + + [Fact] + public void GivenPendingShipmentAndAssignedPickList_WhenNewOrderIsConfirmed_ThenNewPickListIsCreatedAndSingleOrderShipmentIsUpdated() + { + var assessable = new VatRegimes(this.Session).Assessable21; + var vatRate0 = new VatRateBuilder(this.Session).WithRate(0).Build(); + assessable.VatRate = vatRate0; + + var good = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(10).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good.Part).Build(); + + this.Session.Derive(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var mechelenAddress = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build(); + var shipToMechelen = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(mechelenAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var customer = new PersonBuilder(this.Session).WithLastName("customer").WithPartyContactMechanism(shipToMechelen).Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + this.Session.Derive(); + + var order1 = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(mechelenAddress) + .WithVatRegime(assessable) + .Build(); + + var item = new SalesOrderItemBuilder(this.Session) + .WithProduct(good) + .WithQuantityOrdered(3) + .WithAssignedUnitPrice(5) + .Build(); + + order1.AddSalesOrderItem(item); + + this.Session.Derive(); + + order1.SetReadyForPosting(); + this.Session.Derive(); + + order1.Post(); + this.Session.Derive(); + + order1.Accept(); + this.Session.Derive(); + + var shipment = (CustomerShipment)item.OrderShipmentsWhereOrderItem.Single().ShipmentItem.ShipmentWhereShipmentItem; + Assert.Equal(3, shipment.ShipmentItems[0].Quantity); + + shipment.Pick(); + this.Session.Derive(); + + var pickList1 = shipment.ShipmentItems[0].ItemIssuancesWhereShipmentItem[0].PickListItem.PickListWherePickListItem; + Assert.Equal(3, pickList1.PickListItems[0].Quantity); + + pickList1.Picker = this.OrderProcessor; + + this.Session.Derive(); + + var order2 = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(mechelenAddress) + .WithVatRegime(assessable) + .Build(); + + item = new SalesOrderItemBuilder(this.Session) + .WithProduct(good) + .WithQuantityOrdered(2) + .WithAssignedUnitPrice(5) + .Build(); + + order2.AddSalesOrderItem(item); + + this.Session.Derive(); + + order2.SetReadyForPosting(); + this.Session.Derive(); + + order2.Post(); + this.Session.Derive(); + + order2.Accept(); + this.Session.Derive(); + + shipment.Pick(); + this.Session.Derive(); + + Assert.Equal(5, shipment.ShipmentItems.First.Quantity); + + var pickList2 = shipment.ShipmentItems[0].ItemIssuancesWhereShipmentItem[1].PickListItem.PickListWherePickListItem; + Assert.Equal(2, pickList2.PickListItems[0].Quantity); + } + + [Fact] + public void GivenSalesOrderOnHold_WhenInventoryBecomesAvailable_ThenOrderIsNotSelectedForShipment() + { + var assessable = new VatRegimes(this.Session).Assessable21; + var vatRate0 = new VatRateBuilder(this.Session).WithRate(0).Build(); + assessable.VatRate = vatRate0; + + var good = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var mechelenAddress = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build(); + var shipToMechelen = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(mechelenAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var customer = new PersonBuilder(this.Session).WithLastName("customer").WithPartyContactMechanism(shipToMechelen).Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithVatRegime(assessable) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + var item = new SalesOrderItemBuilder(this.Session) + .WithProduct(good) + .WithQuantityOrdered(10) + .WithAssignedUnitPrice(5) + .Build(); + + order.AddSalesOrderItem(item); + + this.Session.Derive(); + + order.SetReadyForPosting(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + order.Hold(); + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).OnHold, order.SalesOrderState); + Assert.Equal(0, item.QuantityPendingShipment); + Assert.Equal(0, item.QuantityRequestsShipping); + Assert.Equal(0, item.QuantityCommittedOut); + Assert.Equal(10, item.QuantityShortFalled); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good.Part).Build(); + + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).OnHold, order.SalesOrderState); + Assert.Equal(0, item.QuantityPendingShipment); + Assert.Equal(0, item.QuantityRequestsShipping); + Assert.Equal(0, item.QuantityCommittedOut); + Assert.Equal(10, item.QuantityShortFalled); + } + + [Fact] + public void GivenSalesOrderOnHold_WhenOrderIsContinued_ThenOrderIsSelectedForShipment() + { + var assessable = new VatRegimes(this.Session).Assessable21; + var vatRate0 = new VatRateBuilder(this.Session).WithRate(0).Build(); + assessable.VatRate = vatRate0; + + var good = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var mechelenAddress = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build(); + var shipToMechelen = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(mechelenAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var customer = new PersonBuilder(this.Session).WithLastName("customer").WithPartyContactMechanism(shipToMechelen).Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithVatRegime(assessable) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + var item = new SalesOrderItemBuilder(this.Session) + .WithProduct(good) + .WithQuantityOrdered(10) + .WithAssignedUnitPrice(5) + .Build(); + + order.AddSalesOrderItem(item); + + this.Session.Derive(); + + order.SetReadyForPosting(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + order.Hold(); + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).OnHold, order.SalesOrderState); + Assert.Equal(0, item.QuantityRequestsShipping); + Assert.Equal(0, item.QuantityPendingShipment); + Assert.Equal(10, item.QuantityShortFalled); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good.Part).Build(); + + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).OnHold, order.SalesOrderState); + Assert.Equal(0, item.QuantityRequestsShipping); + Assert.Equal(0, item.QuantityPendingShipment); + Assert.Equal(10, item.QuantityShortFalled); + + order.Continue(); + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).InProcess, order.SalesOrderState); + Assert.Equal(10, item.QuantityPendingShipment); + Assert.Equal(0, item.QuantityRequestsShipping); + Assert.Equal(0, item.QuantityShortFalled); + } + + [Fact] + public void GivenSalesOrderNotPartiallyShipped_WhenInComplete_ThenOrderIsNotSelectedForShipment() + { + var assessable = new VatRegimes(this.Session).Assessable21; + var vatRate0 = new VatRateBuilder(this.Session).WithRate(0).Build(); + assessable.VatRate = vatRate0; + + var good1 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + var good2 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good2"); + + this.Session.Derive(); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(10).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good1.Part).Build(); + new InventoryItemTransactionBuilder(this.Session).WithQuantity(10).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good2.Part).Build(); + + this.Session.Derive(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var mechelenAddress = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build(); + var shipToMechelen = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(mechelenAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var customer = new PersonBuilder(this.Session).WithLastName("customer").WithPartyContactMechanism(shipToMechelen).Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithVatRegime(assessable) + .WithPartiallyShip(false) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + var item1 = new SalesOrderItemBuilder(this.Session) + .WithProduct(good1) + .WithQuantityOrdered(20) + .WithAssignedUnitPrice(5) + .Build(); + + var item2 = new SalesOrderItemBuilder(this.Session) + .WithProduct(good2) + .WithQuantityOrdered(20) + .WithAssignedUnitPrice(5) + .Build(); + + order.AddSalesOrderItem(item1); + order.AddSalesOrderItem(item2); + + this.Session.Derive(); + + order.SetReadyForPosting(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + Assert.False(customer.ExistShipmentsWhereShipToParty); + + Assert.Equal(10, item1.QuantityRequestsShipping); + Assert.Equal(0, item1.QuantityPendingShipment); + Assert.Equal(10, item1.QuantityShortFalled); + + Assert.Equal(10, item2.QuantityRequestsShipping); + Assert.Equal(0, item2.QuantityPendingShipment); + Assert.Equal(10, item2.QuantityShortFalled); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good1.Part).Build(); + + this.Session.Derive(); + + Assert.False(customer.ExistShipmentsWhereShipToParty); + + Assert.Equal(20, item1.QuantityRequestsShipping); + Assert.Equal(0, item1.QuantityPendingShipment); + Assert.Equal(0, item1.QuantityShortFalled); + + Assert.Equal(10, item2.QuantityRequestsShipping); + Assert.Equal(0, item2.QuantityPendingShipment); + Assert.Equal(10, item2.QuantityShortFalled); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good2.Part).Build(); + + this.Session.Derive(); + + Assert.True(customer.ExistShipmentsWhereShipToParty); + + Assert.Equal(0, item1.QuantityRequestsShipping); + Assert.Equal(20, item1.QuantityPendingShipment); + Assert.Equal(0, item1.QuantityShortFalled); + + Assert.Equal(0, item2.QuantityRequestsShipping); + Assert.Equal(20, item2.QuantityPendingShipment); + Assert.Equal(0, item2.QuantityShortFalled); + } + + [Fact] + public void GivenSalesOrderForStoreExceedingCreditLimit_WhenOrderIsConfirmed_ThenOrderRequestsApproval() + { + var store = this.Session.Extent().First; + store.IsImmediatelyPicked = false; + + var productItem = new InvoiceItemTypes(this.Session).ProductItem; + var contactMechanism = new ContactMechanisms(this.Session).Extent().First; + + var assessable = new VatRegimes(this.Session).Assessable21; + var vatRate0 = new VatRateBuilder(this.Session).WithRate(0).Build(); + assessable.VatRate = vatRate0; + + var good = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good.Part).Build(); + + this.Session.Derive(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var mechelenAddress = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build(); + var shipToMechelen = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(mechelenAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var customer = new PersonBuilder(this.Session).WithLastName("customer").WithPartyContactMechanism(shipToMechelen).Build(); + new CustomerRelationshipBuilder(this.Session) + .WithCustomer(customer) + .WithInternalOrganisation(this.InternalOrganisation) + .WithFromDate(this.Session.Now().AddYears(-2)) + .Build(); + + this.Session.Derive(); + this.Session.Commit(); + + var invoice = new SalesInvoiceBuilder(this.Session) + .WithSalesInvoiceType(new SalesInvoiceTypes(this.Session).SalesInvoice) + .WithBillToCustomer(customer) + .WithBillToContactMechanism(contactMechanism) + .WithInvoiceDate(this.Session.Now().AddYears(-1)) + .Build(); + + var invoiceItem = new SalesInvoiceItemBuilder(this.Session).WithProduct(good).WithQuantity(10).WithAssignedUnitPrice(100).WithInvoiceItemType(productItem).Build(); + invoice.AddSalesInvoiceItem(invoiceItem); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithVatRegime(assessable) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + var item = new SalesOrderItemBuilder(this.Session) + .WithProduct(good) + .WithQuantityOrdered(10) + .WithAssignedUnitPrice(5) + .Build(); + + order.AddSalesOrderItem(item); + + this.Session.Derive(); + + order.SetReadyForPosting(); + + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).RequestsApproval, order.SalesOrderState); + Assert.Equal(0, item.QuantityReserved); + Assert.Equal(0, item.QuantityPendingShipment); + Assert.Equal(0, item.QuantityRequestsShipping); + Assert.Equal(0, item.QuantityShortFalled); + + order.Approve(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).InProcess, order.SalesOrderState); + Assert.Equal(10, item.QuantityReserved); + Assert.Equal(10, item.QuantityPendingShipment); + Assert.Equal(0, item.QuantityRequestsShipping); + Assert.Equal(0, item.QuantityShortFalled); + } + + [Fact] + public void GivenSalesOrderForCustomerExceedingCreditLimit_WhenOrderIsSetReadyForPosting_ThenOrderRequestsApproval() + { + var store = this.Session.Extent().First; + store.IsImmediatelyPicked = false; + + var productItem = new InvoiceItemTypes(this.Session).ProductItem; + var contactMechanism = new ContactMechanisms(this.Session).Extent().First; + + var assessable = new VatRegimes(this.Session).Assessable21; + var vatRate0 = new VatRateBuilder(this.Session).WithRate(0).Build(); + assessable.VatRate = vatRate0; + + var good = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good.Part).Build(); + + this.Session.Derive(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var mechelenAddress = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build(); + var shipToMechelen = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(mechelenAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var customer = new PersonBuilder(this.Session).WithLastName("customer").WithPartyContactMechanism(shipToMechelen).Build(); + + var customerRelationship = new CustomerRelationshipBuilder(this.Session) + .WithCustomer(customer) + .WithInternalOrganisation(this.InternalOrganisation) + .WithFromDate(this.Session.Now().AddYears(-2)) + .Build(); + + this.Session.Derive(); + this.Session.Commit(); + + var partyFinancial = customer.PartyFinancialRelationshipsWhereParty.First(v => Equals(v.InternalOrganisation, customerRelationship.InternalOrganisation)); + partyFinancial.CreditLimit = 100M; + + this.Session.Derive(); + this.Session.Commit(); + + var invoice = new SalesInvoiceBuilder(this.Session) + .WithSalesInvoiceType(new SalesInvoiceTypes(this.Session).SalesInvoice) + .WithBillToCustomer(customer) + .WithBillToContactMechanism(contactMechanism) + .WithInvoiceDate(this.Session.Now().AddYears(-1)) + .Build(); + + var invoiceItem = new SalesInvoiceItemBuilder(this.Session).WithProduct(good).WithQuantity(10).WithAssignedUnitPrice(11).WithInvoiceItemType(productItem).Build(); + invoice.AddSalesInvoiceItem(invoiceItem); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithVatRegime(assessable) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + var item = new SalesOrderItemBuilder(this.Session) + .WithProduct(good) + .WithQuantityOrdered(10) + .WithAssignedUnitPrice(5) + .Build(); + + order.AddSalesOrderItem(item); + + this.Session.Derive(); + + order.SetReadyForPosting(); + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).RequestsApproval, order.SalesOrderState); + Assert.Equal(0, item.QuantityReserved); + Assert.Equal(0, item.QuantityPendingShipment); + Assert.Equal(0, item.QuantityRequestsShipping); + Assert.Equal(0, item.QuantityShortFalled); + + order.Approve(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).InProcess, order.SalesOrderState); + Assert.Equal(10, item.QuantityReserved); + Assert.Equal(10, item.QuantityPendingShipment); + Assert.Equal(0, item.QuantityRequestsShipping); + Assert.Equal(0, item.QuantityShortFalled); + } + + [Fact] + public void GivenSalesOrderBelowOrderThreshold_WhenOrderIsConfirmed_ThenOrderIsNotShipped() + { + new Stores(this.Session).Extent().First.OrderThreshold = 1; + + var assessable = new VatRegimes(this.Session).Assessable21; + var vatRate0 = new VatRateBuilder(this.Session).WithRate(0).Build(); + assessable.VatRate = vatRate0; + + var good = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good.Part).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var mechelenAddress = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build(); + var shipToMechelen = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(mechelenAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var customer = new PersonBuilder(this.Session).WithLastName("customer").WithPartyContactMechanism(shipToMechelen).Build(); + + new CustomerRelationshipBuilder(this.Session) + .WithCustomer(customer) + .WithInternalOrganisation(this.InternalOrganisation) + .Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithVatRegime(assessable) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + var item = new SalesOrderItemBuilder(this.Session) + .WithProduct(good) + .WithQuantityOrdered(1) + .WithAssignedUnitPrice(0.1M) + .Build(); + + order.AddSalesOrderItem(item); + + this.Session.Derive(); + + order.SetReadyForPosting(); + + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).RequestsApproval, order.SalesOrderState); + } + + [Fact] + public void GivenSalesOrderWithManualShipmentSchedule_WhenOrderIsConfirmed_ThenInventoryIsNotReservedAndOrderIsNotShipped() + { + var assessable = new VatRegimes(this.Session).Assessable21; + var vatRate0 = new VatRateBuilder(this.Session).WithRate(0).Build(); + assessable.VatRate = vatRate0; + + var good = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good.Part).Build(); + + this.Session.Derive(); + + var inventoryItem = (NonSerialisedInventoryItem)good.Part.InventoryItemsWherePart.First; + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var mechelenAddress = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build(); + var shipToMechelen = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(mechelenAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var customer = new PersonBuilder(this.Session).WithLastName("customer").WithPartyContactMechanism(shipToMechelen).Build(); + + new CustomerRelationshipBuilder(this.Session).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + this.Session.Derive(); + + var manual = new OrderKindBuilder(this.Session).WithDescription("manual").WithScheduleManually(true).Build(); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithVatRegime(assessable) + .WithOrderKind(manual) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + var item = new SalesOrderItemBuilder(this.Session) + .WithProduct(good) + .WithQuantityOrdered(50) + .WithAssignedUnitPrice(50) + .Build(); + + order.AddSalesOrderItem(item); + + this.Session.Derive(); + + order.SetReadyForPosting(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).InProcess, order.SalesOrderState); + Assert.Equal(0, item.QuantityReserved); + Assert.Equal(0, item.QuantityPendingShipment); + Assert.Equal(0, item.QuantityRequestsShipping); + Assert.Equal(0, item.QuantityShortFalled); + Assert.Equal(100, inventoryItem.QuantityOnHand); + Assert.Equal(100, inventoryItem.AvailableToPromise); + } + + [Fact] + public void GivenConfirmedOrder_WhenOrderIsRejected_ThenNonSerialisedInventoryQuantitiesAreReleased() + { + var billToCustomer = new PersonBuilder(this.Session).WithLastName("person1").Build(); + var shipToCustomer = new PersonBuilder(this.Session).WithLastName("person2").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(billToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(shipToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var shipToContactMechanism = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build(); + + var good1 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + var good2 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good2"); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good1.Part).Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(billToCustomer) + .WithShipToCustomer(shipToCustomer) + .WithShipToAddress(shipToContactMechanism) + .WithBillToContactMechanism(shipToContactMechanism) + .Build(); + + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(1).WithAssignedUnitPrice(15).Build(); + var item2 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(2).WithAssignedUnitPrice(15).Build(); + var item3 = new SalesOrderItemBuilder(this.Session).WithProduct(good2).WithQuantityOrdered(3).WithAssignedUnitPrice(15).Build(); + var item4 = new SalesOrderItemBuilder(this.Session).WithProduct(good2).WithQuantityOrdered(4).WithAssignedUnitPrice(15).Build(); + order.AddSalesOrderItem(item1); + order.AddSalesOrderItem(item2); + order.AddSalesOrderItem(item3); + order.AddSalesOrderItem(item4); + + this.Session.Derive(); + + order.SetReadyForPosting(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + Assert.Equal(3, item1.ReservedFromNonSerialisedInventoryItem.QuantityCommittedOut); + Assert.Equal(97, item1.ReservedFromNonSerialisedInventoryItem.AvailableToPromise); + Assert.Equal(100, item1.ReservedFromNonSerialisedInventoryItem.QuantityOnHand); + Assert.Equal(0, item3.ReservedFromNonSerialisedInventoryItem.QuantityCommittedOut); + Assert.Equal(0, item3.ReservedFromNonSerialisedInventoryItem.AvailableToPromise); + Assert.Equal(0, item3.ReservedFromNonSerialisedInventoryItem.QuantityOnHand); + + order.Reject(); + + this.Session.Derive(); + + Assert.Equal(0, item1.ReservedFromNonSerialisedInventoryItem.QuantityCommittedOut); + Assert.Equal(100, item1.ReservedFromNonSerialisedInventoryItem.AvailableToPromise); + Assert.Equal(100, item1.ReservedFromNonSerialisedInventoryItem.QuantityOnHand); + Assert.Equal(0, item3.ReservedFromNonSerialisedInventoryItem.QuantityCommittedOut); + Assert.Equal(0, item3.ReservedFromNonSerialisedInventoryItem.AvailableToPromise); + Assert.Equal(0, item3.ReservedFromNonSerialisedInventoryItem.QuantityOnHand); + } + + [Fact] + public void GivenConfirmedOrder_WhenOrderIsCancelled_ThenNonSerialisedInventoryQuantitiesAreReleased() + { + var billToCustomer = new PersonBuilder(this.Session).WithLastName("person1").Build(); + var shipToCustomer = new PersonBuilder(this.Session).WithLastName("person2").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(billToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(shipToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var shipToContactMechanism = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build(); + + var good1 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + var good2 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good2"); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good1.Part).Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(billToCustomer) + .WithShipToCustomer(shipToCustomer) + .WithShipToAddress(shipToContactMechanism) + .WithBillToContactMechanism(shipToContactMechanism) + .Build(); + + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(1).WithAssignedUnitPrice(15).Build(); + var item2 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(2).WithAssignedUnitPrice(15).Build(); + var item3 = new SalesOrderItemBuilder(this.Session).WithProduct(good2).WithQuantityOrdered(3).WithAssignedUnitPrice(15).Build(); + var item4 = new SalesOrderItemBuilder(this.Session).WithProduct(good2).WithQuantityOrdered(4).WithAssignedUnitPrice(15).Build(); + order.AddSalesOrderItem(item1); + order.AddSalesOrderItem(item2); + order.AddSalesOrderItem(item3); + order.AddSalesOrderItem(item4); + + this.Session.Derive(); + + order.SetReadyForPosting(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + Assert.Equal(3, item1.ReservedFromNonSerialisedInventoryItem.QuantityCommittedOut); + Assert.Equal(97, item1.ReservedFromNonSerialisedInventoryItem.AvailableToPromise); + Assert.Equal(100, item1.ReservedFromNonSerialisedInventoryItem.QuantityOnHand); + Assert.Equal(0, item3.ReservedFromNonSerialisedInventoryItem.QuantityCommittedOut); + Assert.Equal(0, item3.ReservedFromNonSerialisedInventoryItem.AvailableToPromise); + Assert.Equal(0, item3.ReservedFromNonSerialisedInventoryItem.QuantityOnHand); + + order.Cancel(); + + this.Session.Derive(); + + Assert.Equal(0, item1.ReservedFromNonSerialisedInventoryItem.QuantityCommittedOut); + Assert.Equal(100, item1.ReservedFromNonSerialisedInventoryItem.AvailableToPromise); + Assert.Equal(100, item1.ReservedFromNonSerialisedInventoryItem.QuantityOnHand); + Assert.Equal(0, item3.ReservedFromNonSerialisedInventoryItem.QuantityCommittedOut); + Assert.Equal(0, item3.ReservedFromNonSerialisedInventoryItem.AvailableToPromise); + Assert.Equal(0, item3.ReservedFromNonSerialisedInventoryItem.QuantityOnHand); + } + + [Fact] + public void GivenSalesOrder_WhenBuild_ThenLastObjectStateEqualsCurrencObjectState() + { + var customer = new PersonBuilder(this.Session).WithFirstName("Koen").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).Provisional, order.SalesOrderState); + Assert.Equal(order.LastSalesOrderState, order.SalesOrderState); + } + + [Fact] + public void GivenSalesOrder_WhenBuild_ThenPreviousObjectStateIsNull() + { + var customer = new PersonBuilder(this.Session).WithFirstName("Koen").Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + this.Session.Derive(); + + Assert.Null(order.PreviousSalesOrderState); + } + + [Fact] + public void GivenSalesOrder_WhenConfirmed_ThenCurrentOrderStatusMustBeDerived() + { + var customer = new PersonBuilder(this.Session).WithFirstName("Koen").Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .WithBillToContactMechanism(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).Provisional, order.SalesOrderState); + + order.SetReadyForPosting(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).InProcess, order.SalesOrderState); + } + + [Fact] + public void GivenSalesOrderWithCancelledItem_WhenDeriving_ThenCancelledItemIsNotInValidOrderItems() + { + var billToCustomer = new PersonBuilder(this.Session).WithLastName("person1").Build(); + var shipToCustomer = new PersonBuilder(this.Session).WithLastName("person2").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(billToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(shipToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + + var good1 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + var good2 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good2"); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(billToCustomer) + .WithShipToCustomer(shipToCustomer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .WithBillToContactMechanism(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(3).WithAssignedUnitPrice(15).Build(); + var item2 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(3).WithAssignedUnitPrice(15).Build(); + var item3 = new SalesOrderItemBuilder(this.Session).WithProduct(good2).WithQuantityOrdered(3).WithAssignedUnitPrice(15).Build(); + var item4 = new SalesOrderItemBuilder(this.Session).WithProduct(good2).WithQuantityOrdered(3).WithAssignedUnitPrice(15).Build(); + order.AddSalesOrderItem(item1); + order.AddSalesOrderItem(item2); + order.AddSalesOrderItem(item3); + order.AddSalesOrderItem(item4); + + this.Session.Derive(); + + order.SetReadyForPosting(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + item4.Cancel(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + Assert.Equal(3, order.ValidOrderItems.Count); + Assert.Contains(item1, order.ValidOrderItems); + Assert.Contains(item2, order.ValidOrderItems); + Assert.Contains(item3, order.ValidOrderItems); + } + + [Fact] + public void GivenSalesOrderBuilder_WhenBuild_ThenOrderMustBeValid() + { + var billToCustomer = new PersonBuilder(this.Session).WithLastName("person1").Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(billToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + this.Session.Derive(); + + new SalesOrderBuilder(this.Session).WithBillToCustomer(billToCustomer).WithTakenBy(this.InternalOrganisation).Build(); + + Assert.False(this.Session.Derive(false).HasErrors); + } + + [Fact] + public void GivenSalesOrder_WhenGettingOrderNumberWithoutFormat_ThenOrderNumberShouldBeReturned() + { + var billToCustomer = new PersonBuilder(this.Session).WithLastName("person1").Build(); + var shipToCustomer = new PersonBuilder(this.Session).WithLastName("person2").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(billToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(shipToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + + var store = new Stores(this.Session).Extent().First(v => Equals(v.InternalOrganisation, this.InternalOrganisation)); + store.RemoveSalesOrderNumberPrefix(); + + this.Session.Derive(); + + var order1 = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(billToCustomer) + .WithShipToCustomer(shipToCustomer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .WithStore(store) + .Build(); + + this.Session.Derive(); + + Assert.Equal("1", order1.OrderNumber); + + var order2 = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(billToCustomer) + .WithShipToCustomer(shipToCustomer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .WithStore(store) + .Build(); + + this.Session.Derive(); + + Assert.Equal("2", order2.OrderNumber); + } + + [Fact] + public void GivenSalesOrder_WhenGettingOrderNumberWithFormat_ThenFormattedOrderNumberShouldBeReturned() + { + var billToCustomer = new PersonBuilder(this.Session).WithLastName("person1").Build(); + var shipToCustomer = new PersonBuilder(this.Session).WithLastName("person2").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(billToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(shipToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + + var store = new Stores(this.Session).Extent().First(v => Equals(v.InternalOrganisation, this.InternalOrganisation)); + store.SalesOrderNumberPrefix = "the format is "; + + this.Session.Derive(); + + var order1 = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(billToCustomer) + .WithShipToCustomer(shipToCustomer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .WithStore(store) + .Build(); + + this.Session.Derive(); + + Assert.Equal("the format is 1", order1.OrderNumber); + + var order2 = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(billToCustomer) + .WithShipToCustomer(shipToCustomer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .WithStore(store) + .Build(); + + this.Session.Derive(); + + Assert.Equal("the format is 2", order2.OrderNumber); + } + + [Fact] + public void GivenIssuerWithoutOrderNumberPrefix_WhenDeriving_ThenSortableOrderNumberIsSet() + { + this.InternalOrganisation.StoresWhereInternalOrganisation.First.RemoveSalesOrderNumberPrefix(); + new UnifiedGoodBuilder(this.Session).WithSerialisedDefaults(this.InternalOrganisation).Build(); + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session).WithOrganisationExternalDefaults(this.InternalOrganisation).Build(); + + this.Session.Derive(); + + Assert.Equal(int.Parse(order.OrderNumber), order.SortableOrderNumber); + } + + [Fact] + public void GivenIssuerWithOrderNumberPrefix_WhenDeriving_ThenSortableOrderNumberIsSet() + { + this.InternalOrganisation.StoresWhereInternalOrganisation.First.SalesOrderNumberPrefix = "prefix-"; + new UnifiedGoodBuilder(this.Session).WithSerialisedDefaults(this.InternalOrganisation).Build(); + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session).WithOrganisationExternalDefaults(this.InternalOrganisation).Build(); + + this.Session.Derive(); + + Assert.Equal(int.Parse(order.OrderNumber.Split('-')[1]), order.SortableOrderNumber); + } + + [Fact] + public void GivenIssuerWithParametrizedOrderNumberPrefix_WhenDeriving_ThenSortableOrderNumberIsSet() + { + this.InternalOrganisation.StoresWhereInternalOrganisation.First.SalesOrderNumberPrefix = "prefix-{year}-"; + new UnifiedGoodBuilder(this.Session).WithSerialisedDefaults(this.InternalOrganisation).Build(); + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session).WithOrganisationExternalDefaults(this.InternalOrganisation).Build(); + + this.Session.Derive(); + + Assert.Equal(int.Parse(string.Concat(this.Session.Now().Date.Year.ToString(), order.OrderNumber.Split('-').Last())), order.SortableOrderNumber); + } + + [Fact] + public void GivenSalesOrder_WhenDeriving_ThenTakenByContactMechanismMustExist() + { + var customer = new PersonBuilder(this.Session).WithFirstName("Koen").Build(); + var internalOrganisation = this.InternalOrganisation; + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(internalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var orderContact = new EmailAddressBuilder(this.Session).WithElectronicAddressString("orders@acme.com").Build(); + + var orderFrom = new PartyContactMechanismBuilder(this.Session) + .WithUseAsDefault(true) + .WithContactMechanism(orderContact) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).OrderAddress) + .Build(); + + internalOrganisation.AddPartyContactMechanism(orderFrom); + + this.Session.Derive(); + + var order1 = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithShipToCustomer(customer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + this.Session.Derive(); + + Assert.Equal(internalOrganisation.OrderAddress, order1.TakenByContactMechanism); + } + + [Fact] + public void GivenSalesOrder_WhenDeriving_ThenBillFromContactMechanismMustExist() + { + var customer = new PersonBuilder(this.Session).WithFirstName("Koen").Build(); + var internalOrganisation = this.InternalOrganisation; + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(internalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + + var billingContact = new EmailAddressBuilder(this.Session) + .WithElectronicAddressString("orders@acme.com") + .Build(); + + var billingFrom = new PartyContactMechanismBuilder(this.Session) + .WithUseAsDefault(true) + .WithContactMechanism(billingContact) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).BillingAddress) + .Build(); + + internalOrganisation.AddPartyContactMechanism(billingFrom); + + this.Session.Derive(); + + var order1 = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithShipToCustomer(customer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + this.Session.Derive(); + + Assert.Equal(internalOrganisation.BillingAddress, order1.TakenByContactMechanism); + } + + [Fact] + public void GivenSalesOrderWithBillToCustomerWithPreferredCurrency_WhenBuild_ThenCurrencyIsFromCustomer() + { + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var englischLocale = new Locales(this.Session).EnglishGreatBritain; + var poundSterling = new Currencies(this.Session).FindBy(M.Currency.IsoCode, "GBP"); + + var customer = new OrganisationBuilder(this.Session).WithName("customer").WithLocale(englischLocale).WithPreferredCurrency(poundSterling).Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + this.Session.Derive(); + + Assert.Equal(poundSterling, order.Currency); + + var euro = new Currencies(this.Session).FindBy(M.Currency.IsoCode, "EUR"); + customer.PreferredCurrency = euro; + + this.Session.Derive(); + + Assert.Equal(englischLocale.Country.Currency, order.Currency); + } + + [Fact] + public void GivenSalesOrder_WhenDeriving_ThenLocaleMustExist() + { + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var shipToContactMechanism = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build(); + var customer = new PersonBuilder(this.Session).WithFirstName("Koen").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToAddress(shipToContactMechanism) + .Build(); + + this.Session.Derive(); + + Assert.Equal(this.Session.GetSingleton().DefaultLocale, order.Locale); + } + + [Fact] + public void GivenSalesOrderWithShippingAndHandlingAmount_WhenDeriving_ThenOrderTotalsMustIncludeShippingAndHandlingAmount() + { + var billToCustomer = new PersonBuilder(this.Session).WithLastName("person1").Build(); + var shipToCustomer = new PersonBuilder(this.Session).WithLastName("person2").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(billToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(shipToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var euro = new Currencies(this.Session).FindBy(M.Currency.IsoCode, "EUR"); + var supplier = new OrganisationBuilder(this.Session).WithName("supplier").Build(); + var vatRate21 = new VatRateBuilder(this.Session).WithRate(21).Build(); + var adjustment = new ShippingAndHandlingChargeBuilder(this.Session).WithAmount(7.5M).Build(); + + var good = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + + var supplierOffering = new SupplierOfferingBuilder(this.Session) + .WithPart(good.Part) + .WithSupplier(supplier) + .WithFromDate(this.Session.Now()) + .WithUnitOfMeasure(new UnitsOfMeasure(this.Session).Piece) + .WithPrice(7) + .WithCurrency(euro) + .Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(billToCustomer) + .WithShipToCustomer(shipToCustomer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .WithOrderAdjustment(adjustment) + .Build(); + + const decimal quantityOrdered = 3; + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good).WithQuantityOrdered(quantityOrdered).WithAssignedUnitPrice(15).Build(); + order.AddSalesOrderItem(item1); + + this.Session.Derive(); + + Assert.Equal(45, order.TotalBasePrice); + Assert.Equal(0, order.TotalDiscount); + Assert.Equal(0, order.TotalSurcharge); + Assert.Equal(7.5m, order.TotalShippingAndHandling); + Assert.Equal(0, order.TotalFee); + Assert.Equal(52.5m, order.TotalExVat); + Assert.Equal(11.03m, order.TotalVat); + Assert.Equal(63.53m, order.TotalIncVat); + } + + [Fact] + public void GivenSalesOrderWithShippingAndHandlingPercentage_WhenDeriving_ThenOrderTotalsMustIncludeShippingAndHandlingAmount() + { + var billToCustomer = new PersonBuilder(this.Session).WithLastName("person1").Build(); + var shipToCustomer = new PersonBuilder(this.Session).WithLastName("person2").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(billToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(shipToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var euro = new Currencies(this.Session).FindBy(M.Currency.IsoCode, "EUR"); + var supplier = new OrganisationBuilder(this.Session).WithName("supplier").Build(); + var vatRate21 = new VatRateBuilder(this.Session).WithRate(21).Build(); + var adjustment = new ShippingAndHandlingChargeBuilder(this.Session).WithPercentage(5).Build(); + + var good = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + + var supplierOffering = new SupplierOfferingBuilder(this.Session) + .WithPart(good.Part) + .WithSupplier(supplier) + .WithFromDate(this.Session.Now()) + .WithUnitOfMeasure(new UnitsOfMeasure(this.Session).Piece) + .WithPrice(7) + .WithCurrency(euro) + .Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithBillToCustomer(billToCustomer) + .WithShipToCustomer(shipToCustomer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .WithOrderAdjustment(adjustment) + .Build(); + + const decimal quantityOrdered = 3; + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good).WithQuantityOrdered(quantityOrdered).WithAssignedUnitPrice(15).Build(); + order.AddSalesOrderItem(item1); + + this.Session.Derive(); + + Assert.Equal(45, order.TotalBasePrice); + Assert.Equal(0, order.TotalDiscount); + Assert.Equal(0, order.TotalSurcharge); + Assert.Equal(2.25m, order.TotalShippingAndHandling); + Assert.Equal(0, order.TotalFee); + Assert.Equal(47.25m, order.TotalExVat); + Assert.Equal(9.92m, order.TotalVat); + Assert.Equal(57.17m, order.TotalIncVat); + } + + [Fact] + public void GivenSalesOrderWithFeeAmount_WhenDeriving_ThenOrderTotalsMustIncludeFeeAmount() + { + var billToCustomer = new PersonBuilder(this.Session).WithLastName("person1").Build(); + var shipToCustomer = new PersonBuilder(this.Session).WithLastName("person2").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(billToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(shipToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var euro = new Currencies(this.Session).FindBy(M.Currency.IsoCode, "EUR"); + var supplier = new OrganisationBuilder(this.Session).WithName("supplier").Build(); + var vatRate21 = new VatRateBuilder(this.Session).WithRate(21).Build(); + var adjustment = new FeeBuilder(this.Session).WithAmount(7.5M).Build(); + + var good = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + + var supplierOffering = new SupplierOfferingBuilder(this.Session) + .WithPart(good.Part) + .WithSupplier(supplier) + .WithFromDate(this.Session.Now()) + .WithUnitOfMeasure(new UnitsOfMeasure(this.Session).Piece) + .WithPrice(7) + .WithCurrency(euro) + .Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithBillToCustomer(billToCustomer) + .WithShipToCustomer(shipToCustomer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .WithOrderAdjustment(adjustment) + .Build(); + + const decimal quantityOrdered = 3; + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good).WithQuantityOrdered(quantityOrdered).WithAssignedUnitPrice(15).Build(); + order.AddSalesOrderItem(item1); + + this.Session.Derive(); + + Assert.Equal(45, order.TotalBasePrice); + Assert.Equal(0, order.TotalDiscount); + Assert.Equal(0, order.TotalSurcharge); + Assert.Equal(0, order.TotalShippingAndHandling); + Assert.Equal(7.5m, order.TotalFee); + Assert.Equal(52.5m, order.TotalExVat); + Assert.Equal(11.03m, order.TotalVat); + Assert.Equal(63.53m, order.TotalIncVat); + } + + [Fact] + public void GivenSalesOrderWithoutShipFromAddress_WhenDeriving_ThenUseTakenByShipFromAddress() + { + var billToCustomer = new PersonBuilder(this.Session).WithLastName("person1").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(billToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var vatRate21 = new VatRateBuilder(this.Session).WithRate(21).Build(); + var adjustment = new FeeBuilder(this.Session).WithAmount(7.5M).Build(); + + var order = new SalesOrderBuilder(this.Session) + .WithBillToCustomer(billToCustomer) + .WithOrderAdjustment(adjustment) + .Build(); + + this.Session.Derive(); + + Assert.NotNull(this.InternalOrganisation.ShippingAddress); + Assert.Equal(order.ShipFromAddress, this.InternalOrganisation.ShippingAddress); + } + + [Fact] + public void GivenSalesOrderWithShipFromAddress_WhenDeriving_ThenUseOrderShipFromAddress() + { + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var shipFrom = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build(); + var billToCustomer = new PersonBuilder(this.Session).WithLastName("person1").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(billToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var vatRate21 = new VatRateBuilder(this.Session).WithRate(21).Build(); + var adjustment = new FeeBuilder(this.Session).WithAmount(7.5M).Build(); + + var order = new SalesOrderBuilder(this.Session) + .WithShipFromAddress(shipFrom) + .WithBillToCustomer(billToCustomer) + .WithOrderAdjustment(adjustment) + .Build(); + + this.Session.Derive(); + + Assert.Equal(order.ShipFromAddress, shipFrom); + } + + [Fact] + public void GivenSalesOrderWithFeePercentage_WhenDeriving_ThenOrderTotalsMustIncludeFeeAmount() + { + var billToCustomer = new PersonBuilder(this.Session).WithLastName("person1").Build(); + var shipToCustomer = new PersonBuilder(this.Session).WithLastName("person2").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(billToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(shipToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var euro = new Currencies(this.Session).FindBy(M.Currency.IsoCode, "EUR"); + var supplier = new OrganisationBuilder(this.Session).WithName("supplier").Build(); + var vatRate21 = new VatRateBuilder(this.Session).WithRate(21).Build(); + var adjustment = new FeeBuilder(this.Session).WithPercentage(5).Build(); + + var good = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + + var supplierOffering = new SupplierOfferingBuilder(this.Session) + .WithPart(good.Part) + .WithSupplier(supplier) + .WithFromDate(this.Session.Now()) + .WithUnitOfMeasure(new UnitsOfMeasure(this.Session).Piece) + .WithPrice(7) + .WithCurrency(euro) + .Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithBillToCustomer(billToCustomer) + .WithShipToCustomer(shipToCustomer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .WithOrderAdjustment(adjustment) + .Build(); + + const decimal quantityOrdered = 3; + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good).WithQuantityOrdered(quantityOrdered).WithAssignedUnitPrice(15).Build(); + order.AddSalesOrderItem(item1); + + this.Session.Derive(); + + Assert.Equal(45, order.TotalBasePrice); + Assert.Equal(0, order.TotalDiscount); + Assert.Equal(0, order.TotalSurcharge); + Assert.Equal(0, order.TotalShippingAndHandling); + Assert.Equal(2.25m, order.TotalFee); + Assert.Equal(47.25m, order.TotalExVat); + Assert.Equal(9.92m, order.TotalVat); + Assert.Equal(57.17m, order.TotalIncVat); + } + + [Fact] + public void GivenSalesOrder_WhenConfirming_ThenInventoryItemsQuantityCommittedOutAndAvailableToPromiseMustBeUpdated() + { + var billToCustomer = new PersonBuilder(this.Session).WithLastName("person1").Build(); + var shipToCustomer = new PersonBuilder(this.Session).WithLastName("person2").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(billToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(shipToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var vatRate21 = new VatRateBuilder(this.Session).WithRate(21).Build(); + + var good1 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + var good2 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good2"); + + this.Session.Derive(); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(10).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good1.Part).Build(); + new InventoryItemTransactionBuilder(this.Session).WithQuantity(10).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good2.Part).Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithBillToCustomer(billToCustomer) + .WithShipToCustomer(shipToCustomer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .WithBillToContactMechanism(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(3).WithAssignedUnitPrice(15).Build(); + var item2 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(3).WithAssignedUnitPrice(15).Build(); + var item3 = new SalesOrderItemBuilder(this.Session).WithProduct(good2).WithQuantityOrdered(3).WithAssignedUnitPrice(15).Build(); + order.AddSalesOrderItem(item1); + order.AddSalesOrderItem(item2); + order.AddSalesOrderItem(item3); + + this.Session.Derive(); + + order.SetReadyForPosting(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + Assert.Equal(6, item1.ReservedFromNonSerialisedInventoryItem.QuantityCommittedOut); + Assert.Equal(4, item1.ReservedFromNonSerialisedInventoryItem.AvailableToPromise); + Assert.Equal(3, item3.ReservedFromNonSerialisedInventoryItem.QuantityCommittedOut); + Assert.Equal(7, item3.ReservedFromNonSerialisedInventoryItem.AvailableToPromise); + } + + [Fact] + public void GivenSalesOrder_WhenChangingItemQuantityToZero_ThenItemIsInvalid() + { + var billToCustomer = new PersonBuilder(this.Session).WithLastName("person1").Build(); + var shipToCustomer = new PersonBuilder(this.Session).WithLastName("person2").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(billToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(shipToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + + var good1 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + var good2 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good2"); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithBillToCustomer(billToCustomer) + .WithShipToCustomer(shipToCustomer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .WithBillToContactMechanism(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(1).WithAssignedUnitPrice(15).Build(); + var item2 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(2).WithAssignedUnitPrice(15).Build(); + var item3 = new SalesOrderItemBuilder(this.Session).WithProduct(good2).WithQuantityOrdered(3).WithAssignedUnitPrice(15).Build(); + var item4 = new SalesOrderItemBuilder(this.Session).WithProduct(good2).WithQuantityOrdered(4).WithAssignedUnitPrice(15).Build(); + order.AddSalesOrderItem(item1); + order.AddSalesOrderItem(item2); + order.AddSalesOrderItem(item3); + order.AddSalesOrderItem(item4); + + this.Session.Derive(); + Assert.Equal(4, order.ValidOrderItems.Count); + + order.SetReadyForPosting(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + item4.QuantityOrdered = 0; + + var derivationLog = this.Session.Derive(false); + + Assert.True(derivationLog.HasErrors); + Assert.Contains(M.SalesOrderItem.QuantityOrdered, derivationLog.Errors[0].RoleTypes); + } + + [Fact] + public void GivenSalesOrder_WhenOrderItemIsWithoutBasePrice_ThenExceptionIsThrown() + { + var billToCustomer = new PersonBuilder(this.Session).WithLastName("person1").Build(); + var shipToCustomer = new PersonBuilder(this.Session).WithLastName("person2").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(billToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(shipToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + + var good1 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + var good2 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good2"); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithBillToCustomer(billToCustomer) + .WithShipToCustomer(shipToCustomer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .WithBillToContactMechanism(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + this.Session.Derive(); + + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(1).WithAssignedUnitPrice(15).Build(); + var item2 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(2).WithAssignedUnitPrice(15).Build(); + var item3 = new SalesOrderItemBuilder(this.Session).WithProduct(good2).WithQuantityOrdered(3).WithAssignedUnitPrice(15).Build(); + var item4 = new SalesOrderItemBuilder(this.Session).WithProduct(good2).WithQuantityOrdered(4).WithAssignedUnitPrice(15).Build(); + order.AddSalesOrderItem(item1); + order.AddSalesOrderItem(item2); + order.AddSalesOrderItem(item3); + order.AddSalesOrderItem(item4); + + this.Session.Derive(); + + order.SetReadyForPosting(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + item4.RemoveAssignedUnitPrice(); + + var derivationLog = this.Session.Derive(false); + + Assert.True(derivationLog.HasErrors); + Assert.Contains(M.Priceable.UnitBasePrice, derivationLog.Errors[0].RoleTypes); + + Assert.Contains(item1, order.ValidOrderItems); + Assert.Contains(item2, order.ValidOrderItems); + Assert.Contains(item3, order.ValidOrderItems); + } + + [Fact] + public void GivenSalesOrder_WhenConfirming_ThenAllValidItemsAreInConfirmedState() + { + var billToCustomer = new PersonBuilder(this.Session).WithLastName("person1").Build(); + var shipToCustomer = new PersonBuilder(this.Session).WithLastName("person2").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(billToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(shipToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var shipToContactMechanism = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build(); + + var good1 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + var good2 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good2"); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithBillToCustomer(billToCustomer) + .WithShipToCustomer(shipToCustomer) + .WithShipToAddress(shipToContactMechanism) + .WithBillToContactMechanism(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(1).WithAssignedUnitPrice(15).Build(); + var item2 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(2).WithAssignedUnitPrice(15).Build(); + var item3 = new SalesOrderItemBuilder(this.Session).WithProduct(good2).WithQuantityOrdered(3).WithAssignedUnitPrice(15).Build(); + var item4 = new SalesOrderItemBuilder(this.Session).WithProduct(good2).WithQuantityOrdered(4).WithAssignedUnitPrice(15).Build(); + order.AddSalesOrderItem(item1); + order.AddSalesOrderItem(item2); + order.AddSalesOrderItem(item3); + order.AddSalesOrderItem(item4); + + this.Session.Derive(); + + order.SetReadyForPosting(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + item4.Cancel(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + Assert.Equal(3, order.ValidOrderItems.Count); + Assert.Contains(item1, order.ValidOrderItems); + Assert.Contains(item2, order.ValidOrderItems); + Assert.Contains(item3, order.ValidOrderItems); + Assert.Equal(new SalesOrderItemStates(this.Session).InProcess, item1.SalesOrderItemState); + Assert.Equal(new SalesOrderItemStates(this.Session).InProcess, item2.SalesOrderItemState); + Assert.Equal(new SalesOrderItemStates(this.Session).InProcess, item3.SalesOrderItemState); + Assert.Equal(new SalesOrderItemStates(this.Session).Cancelled, item4.SalesOrderItemState); + } + + [Fact] + public void GivenSalesOrder_WhenConfirmed_ThenShipmentItemsAreCreated() + { + var good1 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + var good2 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good2"); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good1.Part).Build(); + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good2.Part).Build(); + + this.Session.Derive(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var mechelenAddress = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build(); + var shipToMechelen = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(mechelenAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var customer = new PersonBuilder(this.Session).WithFirstName("Koen").WithPartyContactMechanism(shipToMechelen).Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .WithOrderKind(new OrderKindBuilder(this.Session).WithDescription("auto").WithScheduleManually(false).Build()) + .Build(); + + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(1).WithAssignedUnitPrice(15).Build(); + var item2 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(2).WithAssignedUnitPrice(15).Build(); + var item3 = new SalesOrderItemBuilder(this.Session).WithProduct(good2).WithQuantityOrdered(5).WithAssignedUnitPrice(15).Build(); + order.AddSalesOrderItem(item1); + order.AddSalesOrderItem(item2); + order.AddSalesOrderItem(item3); + + this.Session.Derive(); + + order.SetReadyForPosting(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + var shipment = customer.ShipmentsWhereShipToParty.First; + + Assert.Equal(2, shipment.ShipmentItems.Count); + Assert.Equal(1, item1.OrderShipmentsWhereOrderItem[0].Quantity); + Assert.Equal(2, item2.OrderShipmentsWhereOrderItem[0].Quantity); + Assert.Equal(5, item3.OrderShipmentsWhereOrderItem[0].Quantity); + } + + [Fact] + public void GivenSalesOrderForSerialisedItem_WhenConfirmed_ThenShipmentItemIsCreated() + { + var vatRate21 = new VatRateBuilder(this.Session).WithRate(21).Build(); + + var good1 = new NonUnifiedGoodBuilder(this.Session) + .WithName("good1") + .WithProductIdentification(new ProductNumberBuilder(this.Session) + .WithIdentification("good1") + .WithProductIdentificationType(new ProductIdentificationTypes(this.Session).Good).Build()) + .WithVatRate(vatRate21) + .WithPart(new NonUnifiedPartBuilder(this.Session) + .WithProductIdentification(new PartNumberBuilder(this.Session) + .WithIdentification("1") + .WithProductIdentificationType(new ProductIdentificationTypes(this.Session).Part).Build()) + .WithInventoryItemKind(new InventoryItemKinds(this.Session).Serialised) + .Build()) + .Build(); + + var serialisedItem1 = new SerialisedItemBuilder(this.Session).WithSerialNumber("1").WithAvailableForSale(true).Build(); + good1.Part.AddSerialisedItem(serialisedItem1); + + new SerialisedInventoryItemBuilder(this.Session).WithFacility(this.InternalOrganisation.FacilitiesWhereOwner.First).WithPart(good1.Part).WithSerialisedItem(serialisedItem1).Build(); + + this.Session.Derive(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var mechelenAddress = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build(); + var shipToMechelen = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(mechelenAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var customer = new PersonBuilder(this.Session).WithFirstName("Koen").WithPartyContactMechanism(shipToMechelen).Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(1).WithAssignedUnitPrice(15).Build(); + order.AddSalesOrderItem(item1); + + this.Session.Derive(); + order.SetReadyForPosting(); + + //var derivation = new Logging.Derivation(this.Session, new DerivationConfig + // { + // DerivationLogFunc = () => new CustomListDerivationLog(), + // } + //); + + //derivation.Derive(); + + //var list = ((CustomListDerivationLog)derivation.DerivationLog).List; + //list.RemoveAll(v => !v.StartsWith("Dependency")); + + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + var shipment = customer.ShipmentsWhereShipToParty.First; + + Assert.Equal(1, shipment.ShipmentItems.Count); + Assert.Equal(1, item1.OrderShipmentsWhereOrderItem[0].Quantity); + } + + [Fact] + public void GivenSalesOrderWithMultipleRecipients_WhenConfirmed_ThenShipmentIsCreatedForEachRecipientAndPickListIsCreated() + { + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var mechelenAddress = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build(); + var shipToMechelen = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(mechelenAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var baal = new CityBuilder(this.Session).WithName("Baal").Build(); + var baalAddress = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(baal).WithAddress1("Haverwerf 15").Build(); + var shipToBaal = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(baalAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var person1 = new PersonBuilder(this.Session).WithLastName("person1").WithPartyContactMechanism(shipToMechelen).Build(); + var person2 = new PersonBuilder(this.Session).WithLastName("person2").WithPartyContactMechanism(shipToBaal).Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(person1).WithInternalOrganisation(this.InternalOrganisation).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(person2).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var good1 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + var good2 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good2"); + + this.Session.Derive(); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good1.Part).Build(); + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good2.Part).Build(); + + this.Session.Derive(); + + var colorBlack = new ColourBuilder(this.Session) + .WithName("Black") + .Build(); + + var extraLarge = new SizeBuilder(this.Session) + .WithName("Extra large") + .Build(); + + var order = new SalesOrderBuilder(this.Session) + .WithBillToCustomer(person1) + .WithShipToCustomer(person1) + .WithShipToAddress(mechelenAddress) + .WithVatRegime(new VatRegimes(this.Session).Export) + .Build(); + + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(1).WithAssignedUnitPrice(15).Build(); + var item2 = new SalesOrderItemBuilder(this.Session).WithInvoiceItemType(new InvoiceItemTypes(this.Session).ProductFeatureItem).WithProductFeature(colorBlack).WithQuantityOrdered(1).WithAssignedUnitPrice(15).Build(); + var item3 = new SalesOrderItemBuilder(this.Session).WithInvoiceItemType(new InvoiceItemTypes(this.Session).ProductFeatureItem).WithProductFeature(extraLarge).WithQuantityOrdered(1).WithAssignedUnitPrice(15).Build(); + item1.AddOrderedWithFeature(item2); + item1.AddOrderedWithFeature(item3); + var item4 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(2).WithAssignedUnitPrice(15).Build(); + var item5 = new SalesOrderItemBuilder(this.Session).WithProduct(good2).WithQuantityOrdered(5).WithAssignedUnitPrice(15).WithAssignedShipToParty(person2).Build(); + order.AddSalesOrderItem(item1); + order.AddSalesOrderItem(item2); + order.AddSalesOrderItem(item3); + order.AddSalesOrderItem(item4); + order.AddSalesOrderItem(item5); + + this.Session.Derive(); + + order.SetReadyForPosting(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + var shipmentToMechelen = mechelenAddress.ShipmentsWhereShipToAddress[0]; + + var shipmentToBaal = baalAddress.ShipmentsWhereShipToAddress[0]; + + this.Session.Derive(); + + Assert.Equal(mechelenAddress, shipmentToMechelen.ShipToAddress); + Assert.Single(shipmentToMechelen.ShipmentItems); + Assert.Equal(3, shipmentToMechelen.ShipmentItems[0].Quantity); + + Assert.Equal(baalAddress, shipmentToBaal.ShipToAddress); + Assert.Single(shipmentToBaal.ShipmentItems); + Assert.Equal(good2, shipmentToBaal.ShipmentItems[0].Good); + Assert.Equal(5, shipmentToBaal.ShipmentItems[0].Quantity); + } + + [Fact] + public void GivenSalesOrder_WhenShipToAndBillToAreSameCustomer_ThenDerivedCustomersIsSingleCustomer() + { + var customer = new PersonBuilder(this.Session).WithFirstName("Koen").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithShipToCustomer(customer) + .WithBillToCustomer(customer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + this.Session.Derive(); + + Assert.Single(order.Customers); + Assert.Equal(customer, order.Customers.First); + } + + [Fact] + public void GivenSalesOrder_WhenShipToAndBillToAreDifferentCustomers_ThenDerivedCustomersHoldsBothCustomers() + { + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var billToCustomer = new OrganisationBuilder(this.Session).WithName("customer").Build(); + var shipToCustomer = new OrganisationBuilder(this.Session).WithName("customer").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(billToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(shipToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + this.Session.Derive(); + + var order = new SalesOrderBuilder(this.Session) + .WithShipToCustomer(shipToCustomer) + .WithBillToCustomer(billToCustomer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + this.Session.Derive(); + + Assert.Equal(2, order.Customers.Count); + Assert.Contains(billToCustomer, order.Customers); + Assert.Contains(shipToCustomer, order.Customers); + } + + [Fact] + public void GivenSalesOrderTakenByBelgianInternalOrganisationForRentingGoodsToBusinessCustomer_WhenDerived_ThenVatClauseIsSet() + { + var takenByAddress = new PostalAddressBuilder(this.Session) + .WithAddress1("address") + .WithLocality("city") + .WithCountry(new Countries(this.Session).CountryByIsoCode["BE"]) + .Build(); + + var takenByContactMechanism = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(takenByAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).RegisteredOffice) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).BillingAddress) + .WithUseAsDefault(true) + .Build(); + + var shipFromAddress = new PostalAddressBuilder(this.Session) + .WithAddress1("address") + .WithLocality("city") + .WithCountry(new Countries(this.Session).CountryByIsoCode["BE"]) + .Build(); + + var shipFromContactMechanism = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(shipFromAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var belgianInternalOrganisation = new OrganisationBuilder(this.Session) + .WithIsInternalOrganisation(true) + .WithName("Belgian InternalOrganisation") + .WithPartyContactMechanism(takenByContactMechanism) + .WithPartyContactMechanism(shipFromContactMechanism) + .Build(); + + new StoreBuilder(this.Session) + .WithName("store") + .WithBillingProcess(new BillingProcesses(this.Session).BillingForShipmentItems) + .WithInternalOrganisation(belgianInternalOrganisation) + .WithDefaultShipmentMethod(new ShipmentMethods(this.Session).Ground) + .WithDefaultCarrier(new Carriers(this.Session).Fedex) + .WithDefaultCollectionMethod(new PaymentMethods(this.Session).Extent().First) + .WithIsImmediatelyPacked(true) + .Build(); + + var shipToAddress = new PostalAddressBuilder(this.Session) + .WithAddress1("address") + .WithLocality("city") + .WithCountry(new Countries(this.Session).CountryByIsoCode["US"]) + .Build(); + + var shipToContactMechanism = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(shipToAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var customer = new OrganisationBuilder(this.Session).WithName("customer").WithPartyContactMechanism(shipToContactMechanism).WithVatRegime(new VatRegimes(this.Session).Export).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(belgianInternalOrganisation).Build(); + + this.Session.Derive(); + + var good1 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + var good2 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good2"); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithPart(good1.Part).WithReason(new InventoryTransactionReasons(this.Session).Unknown).Build(); + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithPart(good2.Part).WithReason(new InventoryTransactionReasons(this.Session).Unknown).Build(); + + this.Session.Derive(); + + // seller is belgian company, renting good to customer + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(belgianInternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(shipToAddress) + .WithVatRegime(new VatRegimes(this.Session).ServiceB2B) + .Build(); + + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(1).WithAssignedUnitPrice(15).Build(); + var item2 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(2).WithAssignedUnitPrice(15).Build(); + order.AddSalesOrderItem(item1); + order.AddSalesOrderItem(item2); + + this.Session.Derive(); + + Assert.Equal(new VatClauses(this.Session).ServiceB2B, order.DerivedVatClause); + } + + [Fact] + public void GivenSalesOrderTakenByBelgianInternalOrganisationForSellingToInsideEUBusinessCustomer_WhenDerived_ThenVatClauseIsSet() + { + var takenByAddress = new PostalAddressBuilder(this.Session) + .WithAddress1("address") + .WithLocality("city") + .WithCountry(new Countries(this.Session).CountryByIsoCode["BE"]) + .Build(); + + var takenByContactMechanism = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(takenByAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).RegisteredOffice) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).BillingAddress) + .WithUseAsDefault(true) + .Build(); + + var shipFromAddress = new PostalAddressBuilder(this.Session) + .WithAddress1("address") + .WithLocality("city") + .WithCountry(new Countries(this.Session).CountryByIsoCode["BE"]) + .Build(); + + var shipFromContactMechanism = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(shipFromAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var belgianInternalOrganisation = new OrganisationBuilder(this.Session) + .WithIsInternalOrganisation(true) + .WithName("Belgian InternalOrganisation") + .WithPartyContactMechanism(takenByContactMechanism) + .WithPartyContactMechanism(shipFromContactMechanism) + .Build(); + + new StoreBuilder(this.Session) + .WithName("store") + .WithBillingProcess(new BillingProcesses(this.Session).BillingForShipmentItems) + .WithInternalOrganisation(belgianInternalOrganisation) + .WithDefaultShipmentMethod(new ShipmentMethods(this.Session).Ground) + .WithDefaultCarrier(new Carriers(this.Session).Fedex) + .WithDefaultCollectionMethod(new PaymentMethods(this.Session).Extent().First) + .WithIsImmediatelyPacked(true) + .Build(); + + var shipToAddress = new PostalAddressBuilder(this.Session) + .WithAddress1("address") + .WithLocality("city") + .WithCountry(new Countries(this.Session).CountryByIsoCode["NL"]) + .Build(); + + var shipToContactMechanism = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(shipToAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var customer = new OrganisationBuilder(this.Session).WithName("customer").WithPartyContactMechanism(shipToContactMechanism).WithVatRegime(new VatRegimes(this.Session).IntraCommunautair).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(belgianInternalOrganisation).Build(); + + this.Session.Derive(); + + var good1 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + var good2 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good2"); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithPart(good1.Part).WithReason(new InventoryTransactionReasons(this.Session).Unknown).Build(); + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithPart(good2.Part).WithReason(new InventoryTransactionReasons(this.Session).Unknown).Build(); + + this.Session.Derive(); + + // seller is belgian company, selling to EU customer, shipping From Belgium inside EU + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(belgianInternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(shipToAddress) + .Build(); + + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(1).WithAssignedUnitPrice(15).Build(); + var item2 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(2).WithAssignedUnitPrice(15).Build(); + order.AddSalesOrderItem(item1); + order.AddSalesOrderItem(item2); + + this.Session.Derive(); + + Assert.Equal(new VatClauses(this.Session).Intracommunautair, order.DerivedVatClause); + } + + [Fact] + public void GivenSalesOrderTakenByBelgianInternalOrganisationForSellingToOutsideEUBusinessCustomer_WhenDerived_ThenVatClauseIsSet() + { + var takenByAddress = new PostalAddressBuilder(this.Session) + .WithAddress1("address") + .WithLocality("city") + .WithCountry(new Countries(this.Session).CountryByIsoCode["BE"]) + .Build(); + + var takenByContactMechanism = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(takenByAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).RegisteredOffice) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).BillingAddress) + .WithUseAsDefault(true) + .Build(); + + var shipFromAddress = new PostalAddressBuilder(this.Session) + .WithAddress1("address") + .WithLocality("city") + .WithCountry(new Countries(this.Session).CountryByIsoCode["BE"]) + .Build(); + + var shipFromContactMechanism = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(shipFromAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var belgianInternalOrganisation = new OrganisationBuilder(this.Session) + .WithIsInternalOrganisation(true) + .WithName("Belgian InternalOrganisation") + .WithPartyContactMechanism(takenByContactMechanism) + .WithPartyContactMechanism(shipFromContactMechanism) + .Build(); + + new StoreBuilder(this.Session) + .WithName("store") + .WithBillingProcess(new BillingProcesses(this.Session).BillingForShipmentItems) + .WithInternalOrganisation(belgianInternalOrganisation) + .WithDefaultShipmentMethod(new ShipmentMethods(this.Session).Ground) + .WithDefaultCarrier(new Carriers(this.Session).Fedex) + .WithDefaultCollectionMethod(new PaymentMethods(this.Session).Extent().First) + .WithIsImmediatelyPacked(true) + .Build(); + + var shipToAddress = new PostalAddressBuilder(this.Session) + .WithAddress1("address") + .WithLocality("city") + .WithCountry(new Countries(this.Session).CountryByIsoCode["US"]) + .Build(); + + var shipToContactMechanism = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(shipToAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var customer = new OrganisationBuilder(this.Session).WithName("customer").WithPartyContactMechanism(shipToContactMechanism).WithVatRegime(new VatRegimes(this.Session).Export).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(belgianInternalOrganisation).Build(); + + this.Session.Derive(); + + var good1 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + var good2 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good2"); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithPart(good1.Part).WithReason(new InventoryTransactionReasons(this.Session).Unknown).Build(); + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithPart(good2.Part).WithReason(new InventoryTransactionReasons(this.Session).Unknown).Build(); + + this.Session.Derive(); + + // seller is belgian company, selling to outside EU customer, shipping From Belgium outside EU, seller responsible for transport + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(belgianInternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(shipToAddress) + .WithSalesTerm(new IncoTermBuilder(this.Session).WithTermType(new IncoTermTypes(this.Session).Cif).Build()) + .Build(); + + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(1).WithAssignedUnitPrice(15).Build(); + var item2 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(2).WithAssignedUnitPrice(15).Build(); + order.AddSalesOrderItem(item1); + order.AddSalesOrderItem(item2); + + this.Session.Derive(); + + Assert.Equal(new VatClauses(this.Session).BeArt39Par1Item1, order.DerivedVatClause); + } + + [Fact] + public void GivenSalesOrderTakenByBelgianInternalOrganisationForSellingToOutsideEUBusinessCustomerExw_WhenDerived_ThenVatClauseIsSet() + { + var takenByAddress = new PostalAddressBuilder(this.Session) + .WithAddress1("address") + .WithLocality("city") + .WithCountry(new Countries(this.Session).CountryByIsoCode["BE"]) + .Build(); + + var takenByContactMechanism = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(takenByAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).RegisteredOffice) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).BillingAddress) + .WithUseAsDefault(true) + .Build(); + + var shipFromAddress = new PostalAddressBuilder(this.Session) + .WithAddress1("address") + .WithLocality("city") + .WithCountry(new Countries(this.Session).CountryByIsoCode["BE"]) + .Build(); + + var shipFromContactMechanism = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(shipFromAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var belgianInternalOrganisation = new OrganisationBuilder(this.Session) + .WithIsInternalOrganisation(true) + .WithName("Belgian InternalOrganisation") + .WithPartyContactMechanism(takenByContactMechanism) + .WithPartyContactMechanism(shipFromContactMechanism) + .Build(); + + new StoreBuilder(this.Session) + .WithName("store") + .WithBillingProcess(new BillingProcesses(this.Session).BillingForShipmentItems) + .WithInternalOrganisation(belgianInternalOrganisation) + .WithDefaultShipmentMethod(new ShipmentMethods(this.Session).Ground) + .WithDefaultCarrier(new Carriers(this.Session).Fedex) + .WithDefaultCollectionMethod(new PaymentMethods(this.Session).Extent().First) + .WithIsImmediatelyPacked(true) + .Build(); + + var shipToAddress = new PostalAddressBuilder(this.Session) + .WithAddress1("address") + .WithLocality("city") + .WithCountry(new Countries(this.Session).CountryByIsoCode["US"]) + .Build(); + + var shipToContactMechanism = new PartyContactMechanismBuilder(this.Session) + .WithContactMechanism(shipToAddress) + .WithContactPurpose(new ContactMechanismPurposes(this.Session).ShippingAddress) + .WithUseAsDefault(true) + .Build(); + + var customer = new OrganisationBuilder(this.Session).WithName("customer").WithPartyContactMechanism(shipToContactMechanism).WithVatRegime(new VatRegimes(this.Session).Export).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(belgianInternalOrganisation).Build(); + + this.Session.Derive(); + + var good1 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + var good2 = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good2"); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithPart(good1.Part).WithReason(new InventoryTransactionReasons(this.Session).Unknown).Build(); + new InventoryItemTransactionBuilder(this.Session).WithQuantity(100).WithPart(good2.Part).WithReason(new InventoryTransactionReasons(this.Session).Unknown).Build(); + + this.Session.Derive(); + + // seller is belgian company, selling to outside EU customer, shipping From Belgium outside EU, customer responsible for transport + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(belgianInternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(shipToAddress) + .WithSalesTerm(new IncoTermBuilder(this.Session).WithTermType(new IncoTermTypes(this.Session).Exw).Build()) + .Build(); + + var item1 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(1).WithAssignedUnitPrice(15).Build(); + var item2 = new SalesOrderItemBuilder(this.Session).WithProduct(good1).WithQuantityOrdered(2).WithAssignedUnitPrice(15).Build(); + order.AddSalesOrderItem(item1); + order.AddSalesOrderItem(item2); + + this.Session.Derive(); + + Assert.Equal(new VatClauses(this.Session).BeArt39Par1Item2, order.DerivedVatClause); + } + + [Fact] + public void GivenSettingSerialisedItemSoldOnSalesOrderAccept_WhenAcceptingSalesOrder_ThenSerialisedItemStateIsChanged() + { + this.InternalOrganisation.AddSerialisedItemSoldOn(new SerialisedItemSoldOns(this.Session).SalesOrderAccept); + + this.Session.Derive(); + + var vatRate0 = new VatRateBuilder(this.Session).WithRate(0).Build(); + + var good = new NonUnifiedGoodBuilder(this.Session) + .WithName("good1") + .WithProductIdentification(new ProductNumberBuilder(this.Session) + .WithIdentification("good1") + .WithProductIdentificationType(new ProductIdentificationTypes(this.Session).Good).Build()) + .WithVatRate(vatRate0) + .WithPart(new NonUnifiedPartBuilder(this.Session) + .WithProductIdentification(new PartNumberBuilder(this.Session) + .WithIdentification("1") + .WithProductIdentificationType(new ProductIdentificationTypes(this.Session).Part).Build()) + .WithInventoryItemKind(new InventoryItemKinds(this.Session).Serialised) + .Build()) + .Build(); + + var serialisedItem = new SerialisedItemBuilder(this.Session).WithSerialNumber("1").WithAvailableForSale(true).Build(); + good.Part.AddSerialisedItem(serialisedItem); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + var billToCustomer = new OrganisationBuilder(this.Session).WithName("customer").Build(); + var shipToCustomer = new OrganisationBuilder(this.Session).WithName("customer").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(billToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(shipToCustomer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + this.Session.Derive(); + + var address = new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build(); + + var salesOrder = new SalesOrderBuilder(this.Session) + .WithShipToCustomer(shipToCustomer) + .WithBillToCustomer(billToCustomer) + .WithBillToContactMechanism(address) + .WithShipToAddress(address) + .Build(); + + this.Session.Derive(); + + var orderItem = new SalesOrderItemBuilder(this.Session) + .WithProduct(good) + .WithSerialisedItem(serialisedItem) + .WithNextSerialisedItemAvailability(new SerialisedItemAvailabilities(this.Session).Sold) + .WithQuantityOrdered(1) + .WithAssignedUnitPrice(1) + .Build(); + + salesOrder.AddSalesOrderItem(orderItem); + + this.Session.Derive(); + + salesOrder.SetReadyForPosting(); + this.Session.Derive(); + + Assert.NotEqual(new SerialisedItemAvailabilities(this.Session).Sold, serialisedItem.SerialisedItemAvailability); + + salesOrder.Post(); + this.Session.Derive(); + + salesOrder.Accept(); + this.Session.Derive(); + + Assert.Equal(new SerialisedItemAvailabilities(this.Session).Sold, serialisedItem.SerialisedItemAvailability); + } + } + + [Trait("Category", "Security")] + public class SalesOrderTransferSecurityTests : DomainTest + { + public override Config Config => new Config { SetupSecurity = true }; + + [Fact] + public void GivenSalesOrder_WhenObjectStateIsProvisional_ThenCheckTransitions() + { + var customer = new PersonBuilder(this.Session).WithFirstName("Koen").WithUserName("customer").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + + this.Session.Derive(); + + User user = this.Administrator; + this.Session.SetUser(user); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + this.Session.Derive(); + this.Session.Commit(); + + Assert.Equal(new SalesOrderStates(this.Session).Provisional, order.SalesOrderState); + var acl = new AccessControlLists(this.Session.GetUser())[order]; + Assert.True(acl.CanExecute(M.SalesOrder.SetReadyForPosting)); + Assert.True(acl.CanExecute(M.SalesOrder.Cancel)); + Assert.False(acl.CanExecute(M.SalesOrder.Approve)); + Assert.False(acl.CanExecute(M.SalesOrder.Reject)); + Assert.True(acl.CanExecute(M.SalesOrder.Hold)); + Assert.False(acl.CanExecute(M.SalesOrder.Continue)); + Assert.False(acl.CanExecute(M.SalesOrder.Accept)); + Assert.True(acl.CanExecute(M.SalesOrder.DoTransfer)); + } + + [Fact] + public void GivenSalesOrder_WhenObjectStateIsInProcess_ThenCheckTransitions() + { + var customer = new PersonBuilder(this.Session).WithFirstName("Koen").WithUserName("customer").Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + + this.Session.Derive(); + + User user = this.Administrator; + this.Session.SetUser(user); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .WithBillToContactMechanism(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + this.Session.Derive(); + + order.SetReadyForPosting(); + + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + var acl = new AccessControlLists(this.Session.GetUser())[order]; + Assert.True(acl.CanExecute(M.SalesOrder.Cancel)); + Assert.False(acl.CanExecute(M.SalesOrder.Reject)); + Assert.False(acl.CanExecute(M.SalesOrder.Approve)); + Assert.True(acl.CanExecute(M.SalesOrder.Hold)); + Assert.False(acl.CanExecute(M.SalesOrder.SetReadyForPosting)); + Assert.False(acl.CanExecute(M.SalesOrder.Continue)); + } + + [Fact] + public void GivenSalesOrder_WhenObjectStateIsCancelled_ThenCheckTransitions() + { + var customer = new PersonBuilder(this.Session).WithFirstName("Koen").WithUserName("customer").Build(); + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + + this.Session.Derive(); + + this.Session.SetUser(customer); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + this.Session.Derive(); + + order.Cancel(); + + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).Cancelled, order.SalesOrderState); + var acl = new AccessControlLists(this.Session.GetUser())[order]; + Assert.False(acl.CanExecute(M.SalesOrder.SetReadyForPosting)); + Assert.False(acl.CanExecute(M.SalesOrder.Cancel)); + Assert.False(acl.CanExecute(M.SalesOrder.Reject)); + Assert.False(acl.CanExecute(M.SalesOrder.Approve)); + Assert.False(acl.CanExecute(M.SalesOrder.Continue)); + Assert.False(acl.CanExecute(M.SalesOrder.Hold)); + } + + [Fact] + public void GivenSalesOrder_WhenObjectStateIsRejected_ThenCheckTransitions() + { + var customer = new PersonBuilder(this.Session).WithFirstName("Koen").WithUserName("customer").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + + this.Session.Derive(); + + this.Session.SetUser(customer); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + this.Session.Derive(); + + order.Reject(); + + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).Rejected, order.SalesOrderState); + var acl = new AccessControlLists(this.Session.GetUser())[order]; + Assert.False(acl.CanExecute(M.SalesOrder.SetReadyForPosting)); + Assert.False(acl.CanExecute(M.SalesOrder.Cancel)); + Assert.False(acl.CanExecute(M.SalesOrder.Reject)); + Assert.False(acl.CanExecute(M.SalesOrder.Approve)); + Assert.False(acl.CanExecute(M.SalesOrder.Continue)); + Assert.False(acl.CanExecute(M.SalesOrder.Hold)); + } + + [Fact] + public void GivenSalesOrder_WhenObjectStateIsFinished_ThenCheckTransitions() + { + var customer = new PersonBuilder(this.Session).WithFirstName("Koen").WithUserName("customer").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + + this.Session.Derive(); + + this.Session.SetUser(customer); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .WithBillToContactMechanism(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + this.Session.Derive(); + + order.SetReadyForPosting(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + order.SalesOrderState = new SalesOrderStates(this.Session).Finished; + + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).Finished, order.SalesOrderState); + var acl = new AccessControlLists(this.Session.GetUser())[order]; + Assert.False(acl.CanExecute(M.SalesOrder.SetReadyForPosting)); + Assert.False(acl.CanExecute(M.SalesOrder.Cancel)); + Assert.False(acl.CanExecute(M.SalesOrder.Reject)); + Assert.False(acl.CanExecute(M.SalesOrder.Approve)); + Assert.False(acl.CanExecute(M.SalesOrder.Continue)); + Assert.False(acl.CanExecute(M.SalesOrder.Hold)); + } + + [Fact] + public void GivenSalesOrder_WhenObjectStateIsOnHold_ThenCheckTransitions() + { + var customer = new PersonBuilder(this.Session).WithFirstName("Koen").WithUserName("customer").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + + this.Session.Derive(); + + User user = this.Administrator; + this.Session.SetUser(user); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .WithBillToContactMechanism(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + this.Session.Derive(); + + order.SetReadyForPosting(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + order.Hold(); + + this.Session.Derive(); + this.Session.Commit(); + + Assert.Equal(new SalesOrderStates(this.Session).OnHold, order.SalesOrderState); + var acl = new AccessControlLists(this.Session.GetUser())[order]; + Assert.True(acl.CanExecute(M.SalesOrder.Cancel)); + Assert.True(acl.CanExecute(M.SalesOrder.Continue)); + Assert.False(acl.CanExecute(M.SalesOrder.SetReadyForPosting)); + Assert.False(acl.CanExecute(M.SalesOrder.Reject)); + Assert.False(acl.CanExecute(M.SalesOrder.Approve)); + Assert.False(acl.CanExecute(M.SalesOrder.Hold)); + } + + [Fact] + public void GivenSalesOrder_WhenShipmentStateIsInProgress_ThenCancelIsNotAllowed() + { + var customer = new PersonBuilder(this.Session).WithFirstName("Koen").WithUserName("customer").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + + var good = new NonUnifiedGoods(this.Session).FindBy(M.Good.Name, "good1"); + + new InventoryItemTransactionBuilder(this.Session).WithQuantity(1).WithReason(new InventoryTransactionReasons(this.Session).Unknown).WithPart(good.Part).Build(); + + this.Session.Derive(); + + User user = this.Administrator; + this.Session.SetUser(user); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .WithBillToContactMechanism(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + var item = new SalesOrderItemBuilder(this.Session) + .WithProduct(good) + .WithQuantityOrdered(1) + .WithAssignedUnitPrice(5) + .Build(); + + order.AddSalesOrderItem(item); + + this.Session.Derive(); + + order.SetReadyForPosting(); + this.Session.Derive(); + + order.Post(); + this.Session.Derive(); + + order.Accept(); + this.Session.Derive(); + + Assert.Equal(new SalesOrderShipmentStates(this.Session).InProgress, order.SalesOrderShipmentState); + var acl = new AccessControlLists(this.Session.GetUser())[order]; + Assert.False(acl.CanExecute(M.SalesOrder.Cancel)); + } + } +} diff --git a/Base/Database/Domain.Tests/Security/EmployeeTests.cs b/Base/Database/Domain.Tests/Security/EmployeeTests.cs index e992e6a934..ca663a0fce 100644 --- a/Base/Database/Domain.Tests/Security/EmployeeTests.cs +++ b/Base/Database/Domain.Tests/Security/EmployeeTests.cs @@ -117,6 +117,37 @@ public void SalesInvoice() Assert.False(acl.CanWrite(M.SalesInvoice.Description)); } + [Fact] + public void SalesOrder() + { + var customer = new PersonBuilder(this.Session).WithFirstName("Koen").WithUserName("customer").Build(); + + new CustomerRelationshipBuilder(this.Session).WithFromDate(this.Session.Now()).WithCustomer(customer).WithInternalOrganisation(this.InternalOrganisation).Build(); + + var mechelen = new CityBuilder(this.Session).WithName("Mechelen").Build(); + + this.Session.Derive(); + + var employee = new Employments(this.Session).Extent().Select(v => v.Employee).First(); + this.Session.SetUser(employee); + + var order = new SalesOrderBuilder(this.Session) + .WithTakenBy(this.InternalOrganisation) + .WithBillToCustomer(customer) + .WithShipToCustomer(customer) + .WithShipToAddress(new PostalAddressBuilder(this.Session).WithPostalAddressBoundary(mechelen).WithAddress1("Haverwerf 15").Build()) + .Build(); + + this.Session.Derive(); + + Assert.Equal(new SalesOrderStates(this.Session).Provisional, order.SalesOrderState); + + var acl = new AccessControlLists(employee)[order]; + Assert.False(acl.CanExecute(M.SalesOrder.DoTransfer)); + Assert.False(acl.CanWrite(M.SalesOrder.Description)); + Assert.True(acl.CanRead(M.SalesOrder.Description)); + } + [Fact] public void UserGroup() { diff --git a/Base/Database/Domain/Base/Order/SalesOrder.cs b/Base/Database/Domain/Base/Order/SalesOrder.cs index 0a92434c3c..869bd1ef33 100644 --- a/Base/Database/Domain/Base/Order/SalesOrder.cs +++ b/Base/Database/Domain/Base/Order/SalesOrder.cs @@ -593,7 +593,6 @@ public void BaseSetReadyForPosting(SalesOrderSetReadyForPosting method) public void BaseContinue(OrderContinue method) => this.SalesOrderState = this.PreviousSalesOrderState; public void BaseComplete(OrderComplete method) => this.SalesOrderState = new SalesOrderStates(this.Strategy.Session).Completed; - public void BaseShip(SalesOrderShip method) { if (this.CanShip) diff --git a/Base/Database/Domain/Base/Order/SalesOrderStates.cs b/Base/Database/Domain/Base/Order/SalesOrderStates.cs index fcb68725f1..470b680dd9 100644 --- a/Base/Database/Domain/Base/Order/SalesOrderStates.cs +++ b/Base/Database/Domain/Base/Order/SalesOrderStates.cs @@ -17,6 +17,7 @@ public partial class SalesOrderStates internal static readonly Guid OnHoldId = new Guid("f625fb7e-893e-4f68-ab7b-2bc29a644e5b"); internal static readonly Guid CompletedId = new Guid("81F80082-040C-405a-8C01-778868D57C75"); internal static readonly Guid FinishedId = new Guid("DFE75006-81FD-424a-AF58-2528A657155D"); + internal static readonly Guid TransferredId = new Guid("37bda4c2-dcc3-4f50-8a67-22da1142c1c8"); internal static readonly Guid CancelledId = new Guid("8AE3813D-7866-4e1c-AB70-EE695154F8F7"); internal static readonly Guid RejectedId = new Guid("AE2AB1DC-0E5E-4061-924C-025AB84769C0"); @@ -36,6 +37,8 @@ public partial class SalesOrderStates public SalesOrderState Finished => this.Cache[FinishedId]; + public SalesOrderState Transferred => this.Cache[TransferredId]; + public SalesOrderState OnHold => this.Cache[OnHoldId]; public SalesOrderState AwaitingAcceptance => this.Cache[AwaitingAcceptanceId]; @@ -58,6 +61,7 @@ protected override void BaseSetup(Setup setup) merge(AwaitingAcceptanceId, v => v.Name = "Awaiting customer acceptance"); merge(InProcessId, v => v.Name = "In Process"); merge(FinishedId, v => v.Name = "Finished"); + merge(TransferredId, v => v.Name = "Transferred"); } } } diff --git a/Base/Database/Domain/Base/Order/SalesOrderTransfer.cs b/Base/Database/Domain/Base/Order/SalesOrderTransfer.cs index eeb6324b29..cafbe7e19e 100644 --- a/Base/Database/Domain/Base/Order/SalesOrderTransfer.cs +++ b/Base/Database/Domain/Base/Order/SalesOrderTransfer.cs @@ -39,6 +39,159 @@ public void BaseOnDerive(ObjectOnDerive method) { var derivation = method.Derivation; var session = this.Session(); + + if (this.ExistFrom && this.ExistInternalOrganisation && !this.ExistTo) + { + var acl = new AccessControlLists(session.GetUser())[this.From]; + if (acl.CanExecute(M.SalesOrder.DoTransfer)) + { + this.To = this.Transfer(); + this.From.SalesOrderState = new SalesOrderStates(session).Transferred; + } + } + } + + private SalesOrder Transfer() + { + var salesOrder = new SalesOrderBuilder(this.Strategy.Session) + .WithTakenBy(this.InternalOrganisation) + .WithShipToCustomer(this.From.ShipToCustomer) + .WithShipToAddress(this.From.ShipToAddress) + .WithShipToContactPerson(this.ShipToContactPerson) + .WithShipToEndCustomer(this.ShipToEndCustomer) + .WithShipToEndCustomerAddress(this.ShipToEndCustomerAddress) + .WithShipToEndCustomerContactPerson(this.ShipToEndCustomerContactPerson) + .WithDescription(this.Description) + .WithStore(this.Store) + .WithInvoiceDate(this.Session().Now()) + .WithSalesChannel(this.SalesChannel) + .WithSalesInvoiceType(new SalesInvoiceTypes(this.Strategy.Session).SalesInvoice) + .WithVatRegime(this.VatRegime) + .WithIrpfRegime(this.IrpfRegime) + .WithCustomerReference(this.CustomerReference) + .WithPaymentMethod(this.PaymentMethod) + .WithComment(this.Comment) + .WithInternalComment(this.InternalComment) + .WithMessage(this.Message) + .WithBillingAccount(this.BillingAccount) + .Build(); + + foreach (OrderAdjustment orderAdjustment in this.OrderAdjustments) + { + OrderAdjustment newAdjustment = null; + if (orderAdjustment.GetType().Name.Equals(typeof(DiscountAdjustment).Name)) + { + newAdjustment = new DiscountAdjustmentBuilder(this.Session()).Build(); + } + + if (orderAdjustment.GetType().Name.Equals(typeof(SurchargeAdjustment).Name)) + { + newAdjustment = new SurchargeAdjustmentBuilder(this.Session()).Build(); + } + + if (orderAdjustment.GetType().Name.Equals(typeof(Fee).Name)) + { + newAdjustment = new FeeBuilder(this.Session()).Build(); + } + + if (orderAdjustment.GetType().Name.Equals(typeof(ShippingAndHandlingCharge).Name)) + { + newAdjustment = new ShippingAndHandlingChargeBuilder(this.Session()).Build(); + } + + if (orderAdjustment.GetType().Name.Equals(typeof(MiscellaneousCharge).Name)) + { + newAdjustment = new MiscellaneousChargeBuilder(this.Session()).Build(); + } + + newAdjustment.Amount ??= orderAdjustment.Amount; + newAdjustment.Percentage ??= orderAdjustment.Percentage; + salesInvoice.AddOrderAdjustment(newAdjustment); + } + + foreach (SalesInvoiceItem salesInvoiceItem in this.SalesInvoiceItems) + { + var invoiceItem = new SalesInvoiceItemBuilder(this.Strategy.Session) + .WithInvoiceItemType(salesInvoiceItem.InvoiceItemType) + .WithAssignedUnitPrice(salesInvoiceItem.AssignedUnitPrice) + .WithAssignedVatRegime(salesInvoiceItem.AssignedVatRegime) + .WithAssignedIrpfRegime(salesInvoiceItem.AssignedIrpfRegime) + .WithProduct(salesInvoiceItem.Product) + .WithQuantity(salesInvoiceItem.Quantity) + .WithDescription(salesInvoiceItem.Description) + .WithSerialisedItem(salesInvoiceItem.SerialisedItem) + .WithNextSerialisedItemAvailability(salesInvoiceItem.NextSerialisedItemAvailability) + .WithComment(salesInvoiceItem.Comment) + .WithInternalComment(salesInvoiceItem.InternalComment) + .WithMessage(salesInvoiceItem.Message) + .WithFacility(salesInvoiceItem.Facility) + .Build(); + + invoiceItem.ProductFeatures = salesInvoiceItem.ProductFeatures; + salesInvoice.AddSalesInvoiceItem(invoiceItem); + + foreach (SalesTerm salesTerm in salesInvoiceItem.SalesTerms) + { + if (salesTerm.GetType().Name == typeof(IncoTerm).Name) + { + salesInvoiceItem.AddSalesTerm(new IncoTermBuilder(this.Strategy.Session) + .WithTermType(salesTerm.TermType) + .WithTermValue(salesTerm.TermValue) + .WithDescription(salesTerm.Description) + .Build()); + } + + if (salesTerm.GetType().Name == typeof(InvoiceTerm).Name) + { + salesInvoiceItem.AddSalesTerm(new InvoiceTermBuilder(this.Strategy.Session) + .WithTermType(salesTerm.TermType) + .WithTermValue(salesTerm.TermValue) + .WithDescription(salesTerm.Description) + .Build()); + } + + if (salesTerm.GetType().Name == typeof(OrderTerm).Name) + { + salesInvoiceItem.AddSalesTerm(new OrderTermBuilder(this.Strategy.Session) + .WithTermType(salesTerm.TermType) + .WithTermValue(salesTerm.TermValue) + .WithDescription(salesTerm.Description) + .Build()); + } + } + } + + foreach (SalesTerm salesTerm in this.SalesTerms) + { + if (salesTerm.GetType().Name == typeof(IncoTerm).Name) + { + salesInvoice.AddSalesTerm(new IncoTermBuilder(this.Strategy.Session) + .WithTermType(salesTerm.TermType) + .WithTermValue(salesTerm.TermValue) + .WithDescription(salesTerm.Description) + .Build()); + } + + if (salesTerm.GetType().Name == typeof(InvoiceTerm).Name) + { + salesInvoice.AddSalesTerm(new InvoiceTermBuilder(this.Strategy.Session) + .WithTermType(salesTerm.TermType) + .WithTermValue(salesTerm.TermValue) + .WithDescription(salesTerm.Description) + .Build()); + } + + if (salesTerm.GetType().Name == typeof(OrderTerm).Name) + { + salesInvoice.AddSalesTerm(new OrderTermBuilder(this.Strategy.Session) + .WithTermType(salesTerm.TermType) + .WithTermValue(salesTerm.TermValue) + .WithDescription(salesTerm.Description) + .Build()); + } + } + + return salesOrder; } } } diff --git a/Base/Database/Domain/Base/Order/SalesOrders.cs b/Base/Database/Domain/Base/Order/SalesOrders.cs index 3c35ea3382..1242191065 100644 --- a/Base/Database/Domain/Base/Order/SalesOrders.cs +++ b/Base/Database/Domain/Base/Order/SalesOrders.cs @@ -23,6 +23,7 @@ protected override void BaseSecure(Security config) var rejected = new SalesOrderStates(this.Session).Rejected; var completed = new SalesOrderStates(this.Session).Completed; var finished = new SalesOrderStates(this.Session).Finished; + var tranferred = new SalesOrderStates(this.Session).Transferred; var setReadyForPosting = this.Meta.SetReadyForPosting; var post = this.Meta.Post; @@ -47,6 +48,7 @@ protected override void BaseSecure(Security config) config.Deny(this.ObjectType, onHold, setReadyForPosting, reject, approve, hold, ship, invoice, post, accept, revise, transfer); config.Deny(this.ObjectType, rejected, reject, ship, invoice, post, accept, hold, @continue, revise, approve, setReadyForPosting, cancel, transfer); config.Deny(this.ObjectType, cancelled, cancel, ship, invoice, post, accept, hold, @continue, revise, approve, setReadyForPosting, reject, transfer); + config.Deny(this.ObjectType, tranferred, cancel, ship, invoice, post, accept, hold, @continue, revise, approve, setReadyForPosting, reject, transfer); config.Deny(this.ObjectType, completed, complete, reject, cancel, approve, hold, @continue, setReadyForPosting, invoice, post, accept, reopen, revise, transfer); config.Deny(this.ObjectType, awaitingAcceptance, Operations.Write); diff --git a/Base/Workspace/Typescript/Intranet/src/allors/material/base/objects/salesordertransfer/edit/salesterm-edit.component.html b/Base/Workspace/Typescript/Intranet/src/allors/material/base/objects/salesordertransfer/edit/salesterm-edit.component.html new file mode 100644 index 0000000000..72e06cc458 --- /dev/null +++ b/Base/Workspace/Typescript/Intranet/src/allors/material/base/objects/salesordertransfer/edit/salesterm-edit.component.html @@ -0,0 +1,19 @@ +
+ +

{{title}}

+ + + +
+ + +
+ +
+ +
+ + +
+ +
diff --git a/Base/Workspace/Typescript/Intranet/src/allors/material/base/objects/salesordertransfer/edit/salesterm-edit.component.ts b/Base/Workspace/Typescript/Intranet/src/allors/material/base/objects/salesordertransfer/edit/salesterm-edit.component.ts new file mode 100644 index 0000000000..c0b7d5ab07 --- /dev/null +++ b/Base/Workspace/Typescript/Intranet/src/allors/material/base/objects/salesordertransfer/edit/salesterm-edit.component.ts @@ -0,0 +1,119 @@ +import { Component, OnDestroy, OnInit, Self, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { BehaviorSubject, Subscription, combineLatest } from 'rxjs'; + +import { Saved, ContextService, MetaService, RefreshService, TestScope } from '../../../../../angular'; +import { SalesTerm, TermType } from '../../../../../domain'; +import { PullRequest, Sort, Equals, ISessionObject, IObject } from '../../../../../framework'; +import { ObjectData, SaveService } from '../../../../../material'; +import { Meta } from '../../../../../meta'; +import { switchMap, map } from 'rxjs/operators'; + +@Component({ + templateUrl: './salesterm-edit.component.html', + providers: [ContextService] +}) +export class SalesTermEditComponent extends TestScope implements OnInit, OnDestroy { + + public m: Meta; + + public title = 'Edit Term Type'; + + public container: ISessionObject; + public object: SalesTerm; + public termTypes: TermType[]; + + private subscription: Subscription; + + constructor( + @Self() public allors: ContextService, + @Inject(MAT_DIALOG_DATA) public data: ObjectData, + public dialogRef: MatDialogRef, + public metaService: MetaService, + public refreshService: RefreshService, + private saveService: SaveService, + ) { + super(); + + this.m = this.metaService.m; + } + + public ngOnInit(): void { + + const { m, pull, x } = this.metaService; + + this.subscription = combineLatest(this.refreshService.refresh$) + .pipe( + switchMap(() => { + + const create = (this.data as IObject).id === undefined; + const { objectType, associationRoleType } = this.data; + + const pulls = [ + pull.SalesTerm( + { + object: this.data.id, + include: { + TermType: x, + } + }), + pull.TermType({ + predicate: new Equals({ propertyType: m.TermType.IsActive, value: true }), + sort: [ + new Sort(m.TermType.Name), + ], + }) + ]; + + if (create && this.data.associationId) { + pulls.push( + pull.SalesInvoice({ object: this.data.associationId }), + pull.SalesOrder({ object: this.data.associationId }), + ); + } + + return this.allors.context.load(new PullRequest({ pulls })) + .pipe( + map((loaded) => ({ loaded, create, objectType, associationRoleType })) + ); + }) + ) + .subscribe(({ loaded, create, objectType, associationRoleType }) => { + this.allors.context.reset(); + + this.container = loaded.objects.SalesInvoice || loaded.objects.SalesOrder; + this.object = loaded.objects.SalesTerm as SalesTerm; + this.termTypes = loaded.collections.TermTypes as TermType[]; + this.termTypes = this.termTypes.filter(v => v.objectType.name === `${objectType.name}Type`); + + if (create) { + this.title = 'Add Sales Term'; + this.object = this.allors.context.create(objectType.name) as SalesTerm; + this.container.add(associationRoleType, this.object); + } + + }); + } + + public ngOnDestroy(): void { + if (this.subscription) { + this.subscription.unsubscribe(); + } + } + + public save(): void { + + this.allors.context.save() + .subscribe((saved: Saved) => { + const data: IObject = { + id: this.object.id, + objectType: this.object.objectType, + }; + + this.dialogRef.close(data); + this.refreshService.refresh(); + }, + this.saveService.errorHandler + ); + } +} diff --git a/Base/Workspace/Typescript/Intranet/src/allors/material/base/objects/salesordertransfer/edit/salesterm-edit.module.ts b/Base/Workspace/Typescript/Intranet/src/allors/material/base/objects/salesordertransfer/edit/salesterm-edit.module.ts new file mode 100644 index 0000000000..0e3c1787e4 --- /dev/null +++ b/Base/Workspace/Typescript/Intranet/src/allors/material/base/objects/salesordertransfer/edit/salesterm-edit.module.ts @@ -0,0 +1,85 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatOptionModule } from '@angular/material/core'; +import { MatDatepickerModule } from '@angular/material/datepicker'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatListModule } from '@angular/material/list'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatSelectModule } from '@angular/material/select'; +import { MatTabsModule } from '@angular/material/tabs'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { MatTooltipModule } from '@angular/material/tooltip'; + + +import { AllorsMaterialAutoCompleteModule } from '../../../../core/components/role/autocomplete'; + +import { AllorsMaterialChipsModule } from '../../../../core/components/role/chips'; +import { AllorsMaterialDatepickerModule } from '../../../../core/components/role/datepicker'; +import { AllorsMaterialFileModule } from '../../../../core/components/role/file'; +import { AllorsMaterialFilesModule } from '../../../..//core/components/role/files'; +import { AllorsMaterialInputModule } from '../../../../core/components/role/input'; +import { AllorsMaterialLocalisedTextModule } from '../../../../core/components/role/localisedtext'; +import { AllorsMaterialSelectModule } from '../../../../core/components/role/select'; +import { AllorsMaterialSideNavToggleModule } from '../../../../core/components/sidenavtoggle'; +import { AllorsMaterialSlideToggleModule } from '../../../../core/components/role/slidetoggle'; +import { AllorsMaterialStaticModule } from '../../../../core/components/role/static'; +import { AllorsMaterialTextAreaModule } from '../../../../core/components/role/textarea'; + +import { SalesTermEditComponent } from './salesterm-edit.component'; +export { SalesTermEditComponent } from './salesterm-edit.component'; + +@NgModule({ + declarations: [ + SalesTermEditComponent, + ], + exports: [ + SalesTermEditComponent, + ], + imports: [ + AllorsMaterialAutoCompleteModule, + AllorsMaterialChipsModule, + AllorsMaterialDatepickerModule, + AllorsMaterialFileModule, + AllorsMaterialFilesModule, + AllorsMaterialInputModule, + AllorsMaterialLocalisedTextModule, + AllorsMaterialSelectModule, + AllorsMaterialSideNavToggleModule, + AllorsMaterialSlideToggleModule, + AllorsMaterialStaticModule, + AllorsMaterialTextAreaModule, + CommonModule, + + FormsModule, + MatButtonModule, + MatCardModule, + MatDatepickerModule, + MatDialogModule, + MatDividerModule, + MatExpansionModule, + MatFormFieldModule, + MatIconModule, + MatInputModule, + MatListModule, + MatMenuModule, + MatRadioModule, + MatSelectModule, + MatTabsModule, + MatToolbarModule, + MatTooltipModule, + MatOptionModule, + ReactiveFormsModule, + RouterModule, + ], +}) +export class SalesTermEditModule { } diff --git a/Base/Workspace/Typescript/Intranet/src/allors/material/base/objects/salesordertransfer/overview/panel/salesterm-overview-panel.component.html b/Base/Workspace/Typescript/Intranet/src/allors/material/base/objects/salesordertransfer/overview/panel/salesterm-overview-panel.component.html new file mode 100644 index 0000000000..8accfc8e85 --- /dev/null +++ b/Base/Workspace/Typescript/Intranet/src/allors/material/base/objects/salesordertransfer/overview/panel/salesterm-overview-panel.component.html @@ -0,0 +1,34 @@ +
+
+
+
+ {{panel.icon}} +
+
+

{{panel.title}}

+
{{objects?.length}} total sales terms
+
+
+
+
+ +
+ +
+
+
+

Sales Terms

+
+
+ expand_less +
+
+
+ + + +
+ +
+ +
diff --git a/Base/Workspace/Typescript/Intranet/src/allors/material/base/objects/salesordertransfer/overview/panel/salesterm-overview-panel.component.ts b/Base/Workspace/Typescript/Intranet/src/allors/material/base/objects/salesordertransfer/overview/panel/salesterm-overview-panel.component.ts new file mode 100644 index 0000000000..81c46887c0 --- /dev/null +++ b/Base/Workspace/Typescript/Intranet/src/allors/material/base/objects/salesordertransfer/overview/panel/salesterm-overview-panel.component.ts @@ -0,0 +1,150 @@ +import { Component, Self, HostBinding } from '@angular/core'; +import { PanelService, NavigationService, RefreshService, Action, MetaService, TestScope } from '../../../../../../angular'; +import { SalesTerm, SalesInvoice, SalesOrder } from '../../../../../../domain'; +import { Meta } from '../../../../../../meta'; +import { DeleteService, TableRow, Table, EditService } from '../../../../..'; + +import { MatSnackBar } from '@angular/material/snack-bar'; + +import { ObjectData, ObjectService } from '../../../../../../material/core/services/object'; +interface Row extends TableRow { + object: SalesTerm; + name: string; + value: string; +} + +@Component({ + // tslint:disable-next-line:component-selector + selector: 'salesterm-overview-panel', + templateUrl: './salesterm-overview-panel.component.html', + providers: [PanelService] +}) +export class SalesTermOverviewPanelComponent extends TestScope { + container: any; + + @HostBinding('class.expanded-panel') get expandedPanelClass() { + return this.panel.isExpanded; + } + + m: Meta; + + objects: SalesTerm[]; + table: Table; + + delete: Action; + edit: Action; + + get createData(): ObjectData { + return { + associationId: this.panel.manager.id, + associationObjectType: this.panel.manager.objectType, + associationRoleType: this.containerRoleType, + }; + } + + get containerRoleType(): any { + if (this.container.objectType.name === this.m.SalesOrder.name) { + return this.m.SalesOrder.SalesTerms; + } else { + return this.m.SalesInvoice.SalesTerms; + } + } + + constructor( + @Self() public panel: PanelService, + public objectService: ObjectService, + public metaService: MetaService, + public refreshService: RefreshService, + public navigation: NavigationService, + public editService: EditService, + public deleteService: DeleteService, + public snackBar: MatSnackBar + ) { + super(); + + this.m = this.metaService.m; + + panel.name = 'salesterm'; + panel.title = 'Sales Terms'; + panel.icon = 'contacts'; + panel.expandable = true; + + this.delete = deleteService.delete(panel.manager.context); + this.edit = this.editService.edit(); + + const sort = true; + this.table = new Table({ + selection: true, + columns: [ + { name: 'name', sort }, + { name: 'value', sort }, + ], + actions: [ + this.edit, + this.delete, + ], + defaultAction: this.edit, + autoSort: true, + autoFilter: true, + }); + + const salesOrderTermsPullName = `${panel.name}_${this.m.SalesOrder.name}_terms`; + const salesInvoiceTermsPullName = `${panel.name}_${this.m.SalesInvoice.name}_terms`; + const salesOrderPullName = `${panel.name}_${this.m.SalesOrder.name}`; + const salesInvoicePullName = `${panel.name}_${this.m.SalesInvoice.name}`; + + panel.onPull = (pulls) => { + const { pull, x } = this.metaService; + + const id = this.panel.manager.id; + + pulls.push( + pull.SalesOrder({ + name: salesOrderTermsPullName, + object: id, + fetch: { + SalesTerms: { + include: { + TermType: x, + } + } + } + }), + pull.SalesInvoice({ + name: salesInvoiceTermsPullName, + object: id, + fetch: { + SalesTerms: { + include: { + TermType: x, + } + } + } + }), + pull.SalesOrder({ + name: salesOrderPullName, + object: id, + }), + pull.SalesInvoice({ + name: salesInvoicePullName, + object: id, + }), + ); + }; + + panel.onPulled = (loaded) => { + + this.container = loaded.objects[salesOrderPullName] as SalesOrder || loaded.objects[salesInvoicePullName] as SalesInvoice; + this.objects = loaded.collections[salesOrderTermsPullName] as SalesTerm[] || loaded.collections[salesInvoiceTermsPullName] as SalesTerm[]; + this.table.total = loaded.values[`${salesOrderTermsPullName}_total`] || loaded.values[`${salesInvoiceTermsPullName}_total`] || this.objects.length; + this.table.data = this.objects.map((v) => { + return { + object: v, + name: v.TermType && v.TermType.Name, + value: v.TermValue, + } as Row; + }); + }; + + } +} diff --git a/Base/Workspace/Typescript/Intranet/src/allors/material/base/objects/salesordertransfer/overview/panel/salesterm-overview-panel.module.ts b/Base/Workspace/Typescript/Intranet/src/allors/material/base/objects/salesordertransfer/overview/panel/salesterm-overview-panel.module.ts new file mode 100644 index 0000000000..50a29dcb7c --- /dev/null +++ b/Base/Workspace/Typescript/Intranet/src/allors/material/base/objects/salesordertransfer/overview/panel/salesterm-overview-panel.module.ts @@ -0,0 +1,59 @@ +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { CommonModule } from '@angular/common'; +import { RouterModule } from '@angular/router'; +import { MatButtonModule } from '@angular/material/button'; +import { MatButtonToggleModule } from '@angular/material/button-toggle'; +import { MatCardModule } from '@angular/material/card'; +import { MatOptionModule } from '@angular/material/core'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatListModule } from '@angular/material/list'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatSelectModule } from '@angular/material/select'; +import { MatToolbarModule } from '@angular/material/toolbar'; +import { MatTooltipModule } from '@angular/material/tooltip'; + +import { AllorsMaterialFileModule, AllorsMaterialTableModule, AllorsMaterialFactoryFabModule } from '../../../../..'; + +import { SalesTermOverviewPanelComponent as SalesTermOverviewPanelComponent } from './salesterm-overview-panel.component'; +export { SalesTermOverviewPanelComponent as RequestItemOverviewPanelComponent } from './salesterm-overview-panel.component'; + +@NgModule({ + declarations: [ + SalesTermOverviewPanelComponent, + ], + exports: [ + SalesTermOverviewPanelComponent, + ], + imports: [ + CommonModule, + FormsModule, + ReactiveFormsModule, + RouterModule, + + MatButtonModule, + MatButtonToggleModule, + MatCardModule, + MatDividerModule, + MatFormFieldModule, + MatIconModule, + MatInputModule, + MatListModule, + MatMenuModule, + MatRadioModule, + MatSelectModule, + MatToolbarModule, + MatTooltipModule, + MatButtonToggleModule, + MatOptionModule, + + AllorsMaterialFactoryFabModule, + AllorsMaterialFileModule, + AllorsMaterialTableModule, + ], +}) +export class SalesTermOverviewPanelModule { }