using Comal.Classes; using InABox.Core; using InABox.Core.Postable; using InABox.Poster.MYOB; using InABox.Scripting; using MYOB.AccountRight.SDK.Contracts.Version2.Sale; using MYOB.AccountRight.SDK.Services.GeneralLedger; using MYOB.AccountRight.SDK.Services.Purchase; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using MYOBPurchaseOrder = MYOB.AccountRight.SDK.Contracts.Version2.Purchase.ServicePurchaseOrder; using MYOBPurchaseOrderItem = MYOB.AccountRight.SDK.Contracts.Version2.Purchase.ServicePurchaseOrderLine; using MYOBTaxCode = MYOB.AccountRight.SDK.Contracts.Version2.GeneralLedger.TaxCode; namespace PRS.Shared.Posters.MYOB; public class PurchaseOrderMYOBPosterSettings : MYOBPosterSettings { public override string DefaultScript(Type TPostable) { return @"using MYOBPurchaseOrder = MYOB.AccountRight.SDK.Contracts.Version2.Purchase.ServicePurchaseOrder; using MYOBPurchaseOrderItem = MYOB.AccountRight.SDK.Contracts.Version2.Purchase.ServicePurchaseOrderLine; public class Module { public void BeforePost(IDataModel model) { // Perform pre-processing } public void ProcessPurchaseOrder(IDataModel model, PurchaseOrder purchaseOrder, MYOBPurchaseOrder myobPurchaseOrder) { // Do extra processing for a purchase order; throw an exception to fail this purchase order. } public void ProcessPurchaseOrderItem(IDataModel model, PurchaseOrderItem purchaseOrderItem, MYOBPurchaseOrderItem myobPurchaseOrderItem) { // Do extra processing for a purchase order item; throw an exception to fail this purchase order item (and thus fail the entire purchase order) } }"; } } public class PurchaseOrderMYOBPoster : IMYOBPoster { public ScriptDocument? Script { get; set; } public MYOBConnectionData ConnectionData { get; set; } public PurchaseOrderMYOBPosterSettings Settings { get; set; } public MYOBGlobalPosterSettings GlobalSettings { get; set; } public bool BeforePost(IDataModel model) { foreach (var (_, table) in model.ModelTables) { table.IsDefault = false; } model.SetIsDefault(true); model.SetColumns(RequiredPurchaseOrderColumns()); model.SetIsDefault(true, alias: "PurchaseOrder_PurchaseOrderItem"); model.SetColumns(RequiredPurchaseOrderItemColumns(), alias: "PurchaseOrder_PurchaseOrderItem"); model.SetIsDefault(true, alias: "PurchaseOrder_Supplier"); model.SetColumns(RequiredSupplierColumns(), alias: "PurchaseOrder_Supplier"); Script?.Execute(methodname: "BeforePost", parameters: new object[] { model }); return true; } #region Script Functions private Result ProcessPurchaseOrder(IDataModel model, PurchaseOrder po, MYOBPurchaseOrder myobPO) { return this.WrapScript("ProcessPurchaseOrder", model, po, myobPO); } private Result ProcessPurchaseOrderItem(IDataModel model, PurchaseOrderItem poItem, MYOBPurchaseOrderItem myobPOItem) { return this.WrapScript("ProcessPurchaseOrderItem", model, poItem, myobPOItem); } #endregion private static Columns RequiredPurchaseOrderColumns() { return Columns.None() .Add(x => x.ID) .Add(x => x.PostedReference) .Add(x => x.IssuedDate) .Add(x => x.PONumber) .Add(x => x.SupplierLink.ID) .Add(x => x.Description); } private static Columns RequiredPurchaseOrderItemColumns() { return Columns.None() .Add(x => x.ID) .Add(x => x.PurchaseOrderLink.ID) .Add(x => x.Description) .Add(x => x.IncTax) .Add(x => x.PurchaseGL.ID) .Add(x => x.PurchaseGL.Code) .Add(x => x.PurchaseGL.PostedReference) .Add(x => x.TaxCode.ID) .Add(x => x.TaxCode.Code) .Add(x => x.TaxCode.PostedReference); } private static Columns RequiredSupplierColumns() { return SupplierMYOBPoster.RequiredColumns(); } public IPostResult Process(IDataModel model) { var results = new PostResult(); var service = new ServicePurchaseOrderService(ConnectionData.Configuration, null, ConnectionData.AuthKey); var pos = model.GetTable().ToArray(); if(pos.Any(x => x.IssuedDate.IsEmpty())) { throw new PostFailedMessageException($"We can't process unissued purchase orders; please set [{nameof(PurchaseOrder.IssuedDate)}] for" + $" all purchase orders before processing."); } var poItems = model.GetTable("PurchaseOrder_PurchaseOrderItem") .ToObjects().GroupBy(x => x.PurchaseOrderLink.ID).ToDictionary(x => x.Key, x => x.ToArray()); var suppliers = model.GetTable("PurchaseOrder_Supplier") .ToObjects().ToDictionary(x => x.ID); foreach(var po in pos) { MYOBPurchaseOrder myobPO; Exception? error; bool isNew; if(Guid.TryParse(po.PostedReference, out var myobID)) { if(!service.Get(ConnectionData, myobID).Get(out var newPO, out error)) { CoreUtils.LogException("", error, $"Failed to find Purchase Order in MYOB with id {myobID}"); results.AddFailed(po, $"Failed to find Purchase Order in MYOB with id {myobID}: {error.Message}"); continue; } myobPO = newPO; isNew = false; } else { myobPO = new MYOBPurchaseOrder(); isNew = true; } myobPO.Number = po.PONumber.Truncate(13); myobPO.Date = po.IssuedDate; // myobPO.SupplierInvoiceNumber if(suppliers.TryGetValue(po.SupplierLink.ID, out var supplier)) { if(!SupplierMYOBPoster.MapSupplier(ConnectionData, supplier, GlobalSettings).Get(out var supplierID, out error)) { CoreUtils.LogException("", error, $"Error while posting purchase order {po.ID}"); results.AddFailed(po, error.Message); continue; } myobPO.Supplier ??= new(); myobPO.Supplier.UID = supplierID; results.AddFragment(supplier, supplier.PostedStatus); } // myobPO.ShipToAddress = // myobPO.Terms = myobPO.IsTaxInclusive = true; myobPO.IsReportable = true; // myobPO.Freight = // myobPO.FreightForeign = if (isNew) { if(!this.GetDefaultTaxCode().Get(out var taxCodeID, out error)) { results.AddFailed(po, error.Message); continue; } myobPO.FreightTaxCode ??= new(); myobPO.FreightTaxCode.UID = taxCodeID; } // myobPO.Category = myobPO.Comment = po.Description.Truncate(2000); // myobPO.ShippingMethod = // myobPO.PromisedDate = // myobPO.JournalMemo = // myobPO.OrderDeliveryStatus = if(poItems.TryGetValue(po.ID, out var lines)) { var newLines = new MYOBPurchaseOrderItem[lines.Length]; string? failed = null; for(int i = 0; i < lines.Length; ++i) { var poItem = lines[i]; var line = new MYOBPurchaseOrderItem(); line.Type = OrderLineType.Transaction; line.Description = poItem.Description.Truncate(1000); if(poItem.PurchaseGL.ID == Guid.Empty) { failed = "Not all lines have a PurchaseGL code set."; break; } if(!Guid.TryParse(poItem.PurchaseGL.PostedReference, out var accountID)) { if(AccountMYOBUtils.GetAccount(ConnectionData, poItem.PurchaseGL.Code).Get(out accountID, out error)) { results.AddFragment(new GLCode { PostedReference = accountID.ToString() }.SetID(poItem.PurchaseGL.ID)); } else { CoreUtils.LogException("", error); failed = error.Message; break; } } line.Account ??= new(); line.Account.UID = accountID; line.Total = Math.Round(Convert.ToDecimal(poItem.IncTax), 2); line.TotalForeign = 0; // line.UnitsOfMeasure = // line.UnitCount = // line.UnitPrice = // line.UnitPriceForeign = // line.DiscountPercent = // line.Job = if(poItem.TaxCode.ID == Guid.Empty) { failed = "Not all lines have a TaxCode set."; break; } if(!Guid.TryParse(poItem.TaxCode.PostedReference, out var taxCodeID)) { if (!ConnectionData.GetUID( new Filter(x => x.Code).IsEqualTo(poItem.TaxCode.Code)) .Get(out taxCodeID, out error)) { CoreUtils.LogException("", error, $"Failed to find TaxCode in MYOB with code {poItem.TaxCode.Code}"); failed = $"Failed to find TaxCode in MYOB with code {poItem.TaxCode.Code}: {error.Message}"; break; } else if (taxCodeID == Guid.Empty) { failed = $"Failed to find TaxCode in MYOB with code {poItem.TaxCode.Code}"; break; } results.AddFragment(new TaxCode { PostedReference = taxCodeID.ToString() }.SetID(taxCodeID)); } line.TaxCode ??= new(); line.TaxCode.UID = taxCodeID; if(!ProcessPurchaseOrderItem(model, poItem, line).Get(out error)) { failed = error.Message; break; } newLines[i] = line; } if(failed is not null) { results.AddFailed(po, failed); continue; } myobPO.Lines = newLines; } else { myobPO.Lines = []; } if(!ProcessPurchaseOrder(model, po, myobPO).Get(out error)) { results.AddFailed(po, error.Message); continue; } if(service.Save(ConnectionData, myobPO).Get(out var result, out error)) { po.PostedReference = result.UID.ToString(); results.AddSuccess(po); } else { CoreUtils.LogException("", error, $"Error while posting purchase order {po.ID}"); results.AddFailed(po, error.Message); } } return results; } } public class PurchaseOrderMYOBPosterEngine : MYOBPosterEngine { }