using Comal.Classes; using InABox.Core; using InABox.Poster.MYOB; using InABox.Scripting; using MYOB.AccountRight.SDK.Contracts.Version2.Sale; using MYOB.AccountRight.SDK.Services.Contact; using MYOB.AccountRight.SDK.Services.GeneralLedger; using MYOB.AccountRight.SDK.Services.Sale; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using MYOB.AccountRight.SDK.Contracts.Version2; using Invoice = Comal.Classes.Invoice; using InvoiceLine = Comal.Classes.InvoiceLine; using MYOBAccount = MYOB.AccountRight.SDK.Contracts.Version2.GeneralLedger.Account; using MYOBInvoice = MYOB.AccountRight.SDK.Contracts.Version2.Sale.ServiceInvoice; using MYOBInvoiceLine = MYOB.AccountRight.SDK.Contracts.Version2.Sale.ServiceInvoiceLine; using MYOBTaxCode = MYOB.AccountRight.SDK.Contracts.Version2.GeneralLedger.TaxCode; namespace PRS.Shared.Posters.MYOB; public class InvoiceMYOBPosterSettings : MYOBPosterSettings { public override string DefaultScript(Type TPostable) { return @"using MYOBInvoice = MYOB.AccountRight.SDK.Contracts.Version2.Sale.ServiceInvoice; using MYOBInvoiceLine = MYOB.AccountRight.SDK.Contracts.Version2.Sale.ServiceInvoiceLine; public class Module { public void BeforePost(IDataModel model) { // Perform pre-processing } public void ProcessInvoice(IDataModel model, Invoice invoice, MYOBInvoice myobInvoice) { // Do extra processing for an invoice; throw an exception to fail this invoice. } public void ProcessInvoiceLine(IDataModel model, InvoiceLine invoiceLine, MYOBInvoiceLine myobInvoiceLine) { // Do extra processing for an invoice line; throw an exception to fail this invoice line (and thus fail the entire invoice) } }"; } } public class InvoiceMYOBPoster : IMYOBPoster { public ScriptDocument? Script { get; set; } public MYOBConnectionData ConnectionData { get; set; } public InvoiceMYOBPosterSettings 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(RequiredInvoiceColumns()); model.SetIsDefault(true, alias: "Invoice_InvoiceLine"); model.SetColumns(RequiredInvoiceLineColumns(), alias: "Invoice_InvoiceLine"); model.SetIsDefault(true, alias: "Invoice_Customer"); model.SetColumns(RequiredCustomerColumns(), alias: "Invoice_Customer"); Script?.Execute(methodname: "BeforePost", parameters: new object[] { model }); return true; } #region Script Functions private Result ProcessInvoice(IDataModel model, Invoice invoice, MYOBInvoice myobInvoice) { return this.WrapScript("ProcessInvoice", model, invoice, myobInvoice); } private Result ProcessInvoiceLine(IDataModel model, InvoiceLine invoiceLine, MYOBInvoiceLine myobInvoiceLine) { return this.WrapScript("ProcessInvoiceLine", model, invoiceLine, myobInvoiceLine); } #endregion private static Columns RequiredInvoiceColumns() { return Columns.None() .Add(x => x.ID) .Add(x => x.PostedReference) .Add(x => x.Number) .Add(x => x.Date) .Add(x => x.DueDate) .Add(x => x.CustomerLink.ID) .Add(x => x.Description); } private static Columns RequiredInvoiceLineColumns() { return Columns.None() .Add(x => x.ID) .Add(x => x.InvoiceLink.ID) .Add(x => x.Description) .Add(x => x.IncTax) .Add(x => x.SellGL.ID) .Add(x => x.SellGL.Code) .Add(x => x.SellGL.PostedReference) .Add(x => x.TaxCode.ID) .Add(x => x.TaxCode.Code) .Add(x => x.TaxCode.PostedReference); } private static Columns RequiredCustomerColumns() { return CustomerMYOBPoster.RequiredColumns(); } public IPostResult Process(IDataModel model) { // Documentation: https://developer.myob.com/api/myob-business-api/v2/sale/invoice/invoice_service/ var results = new PostResult(); var service = new ServiceInvoiceService(ConnectionData.Configuration, null, ConnectionData.AuthKey); var invoices = model.GetTable().ToArray(); var customers = model.GetTable("Invoice_Customer") .ToObjects().ToDictionary(x => x.ID); var invoiceLines = model.GetTable("Invoice_InvoiceLine") .ToObjects() .GroupBy(x => x.InvoiceLink.ID) .ToDictionary(x => x.Key, x => x.ToArray()); foreach(var invoice in invoices) { MYOBInvoice myobInvoice; Exception? error; if(Guid.TryParse(invoice.PostedReference, out var myobID)) { if(!service.Get(ConnectionData, myobID).Get(out var newInvoice, out error)) { CoreUtils.LogException("", error, $"Failed to find Invoice in MYOB with id {myobID}"); results.AddFailed(invoice, $"Failed to find Invoice in MYOB with id {myobID}: {error.Message}"); continue; } myobInvoice = newInvoice; } else { myobInvoice = new MYOBInvoice(); } myobInvoice.Number = invoice.Number.ToString().Truncate(13); myobInvoice.Date = invoice.Date; myobInvoice.Terms ??= new(); myobInvoice.Terms.PaymentIsDue = TermsPaymentType.InAGivenNumberOfDays; myobInvoice.Terms.BalanceDueDate = (invoice.DueDate.Date - invoice.Date.Date).Days + 1; // myobInvoice.CustomerPurchaseOrderNumber = if(customers.TryGetValue(invoice.CustomerLink.ID, out var customer)) { if(!CustomerMYOBPoster.MapCustomer(ConnectionData, customer, GlobalSettings).Get(out var customerID, out error)) { CoreUtils.LogException("", error, $"Error while posting invoice {invoice.ID}"); results.AddFailed(invoice, error.Message); continue; } myobInvoice.Customer ??= new(); myobInvoice.Customer.UID = customerID; results.AddFragment(customer, customer.PostedStatus); } // myobInvoice.PromisedDate = if(invoiceLines.TryGetValue(invoice.ID, out var lines)) { var newLines = new MYOBInvoiceLine[lines.Length]; string? failed = null; for(int i = 0; i < lines.Length; ++i) { var item = lines[i]; var line = new MYOBInvoiceLine(); line.Type = InvoiceLineType.Transaction; line.Description = item.Description.Truncate(1000); // line.UnitOfMeasure = // line.UnitCount = // line.UnitPrice = // line.UnitPriceForeign = // line.DiscountPercent = line.Total = Math.Round(Convert.ToDecimal(item.IncTax),2); line.TotalForeign = 0; if(item.SellGL.ID == Guid.Empty) { failed = "Not all lines have a SellGL code set."; break; } if(!Guid.TryParse(item.SellGL.PostedReference, out var accountID)) { if(AccountMYOBUtils.GetAccount(ConnectionData, item.SellGL.Code).Get(out accountID, out error)) { results.AddFragment(new GLCode { PostedReference = accountID.ToString() }.SetID(item.SellGL.ID)); } else { CoreUtils.LogException("", error); failed = error.Message; break; } } line.Account ??= new(); line.Account.UID = accountID; if(item.TaxCode.ID == Guid.Empty) { failed = "Not all lines have a TaxCode set."; break; } if(!Guid.TryParse(item.TaxCode.PostedReference, out var taxCodeID)) { if (!ConnectionData.GetUID( new Filter(x => x.Code).IsEqualTo(item.TaxCode.Code)) .Get(out taxCodeID, out error)) { CoreUtils.LogException("", error, $"Failed to find TaxCode in MYOB with code {item.TaxCode.Code}"); failed = $"Failed to find TaxCode in MYOB with code {item.TaxCode.Code}: {error.Message}"; break; } else if (taxCodeID == Guid.Empty) { failed = $"Failed to find TaxCode in MYOB with code {item.TaxCode.Code}"; break; } results.AddFragment(new TaxCode { PostedReference = taxCodeID.ToString() }.SetID(taxCodeID)); } line.TaxCode ??= new(); line.TaxCode.UID = taxCodeID; if(!ProcessInvoiceLine(model, item, line).Get(out error)) { failed = error.Message; break; } newLines[i] = line; } if(failed is not null) { results.AddFailed(invoice, failed); continue; } myobInvoice.Lines = newLines; } else { myobInvoice.Lines = []; } // ShipToAddress // Terms myobInvoice.IsTaxInclusive = true; // Freight // FreightTaxCode // Category // Salesperson myobInvoice.Comment = invoice.Description.Truncate(2000); // ShippingMethod // JournalMemo // ReferralSource // InvoiceDeliveryStatus // CanApplySurcharge myobInvoice.InvoiceType = InvoiceLayoutType.Service; // Order // OnlinePaymentMethod if(!ProcessInvoice(model, invoice, myobInvoice).Get(out error)) { results.AddFailed(invoice, error.Message); continue; } if(service.Save(ConnectionData, myobInvoice).Get(out var result, out error)) { invoice.PostedReference = result.UID.ToString(); results.AddSuccess(invoice); } else { CoreUtils.LogException("", error, $"Error while posting invoice {invoice.ID}"); results.AddFailed(invoice, error.Message); } } return results; } } public class InvoiceMYOBPosterEngine : MYOBPosterEngine { }