using Comal.Classes; using FastReport.Data; using InABox.Core; using InABox.Core.Postable; using InABox.Poster.MYOB; using MYOB.AccountRight.SDK.Services; using MYOB.AccountRight.SDK.Services.Contact; using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; using Customer = Comal.Classes.Customer; using MYOBCustomer = MYOB.AccountRight.SDK.Contracts.Version2.Contact.Customer; using MYOBAddress = MYOB.AccountRight.SDK.Contracts.Version2.Contact.Address; using MYOB.AccountRight.SDK.Contracts.Version2.Sale; using InABox.Clients; namespace PRS.Shared.Posters.MYOB; public class CustomerMYOBPosterSettings : MYOBPosterSettings { [TextBoxEditor(ToolTip = "The MYOB tax code which should be used when posting customers")] public string DefaultTaxCode { get; set; } } public static class ContactMYOBUtils { public static void SplitName(string name, out string firstName, out string lastName) { var names = name.Split(' ', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); firstName = names.Length > 0 ? names[0] : ""; lastName = names.Length > 1 ? names[1] : ""; } public static MYOBAddress ConvertAddress(Address address, int location, IContact contact) { return new MYOBAddress { Location = location, Street = address.Street.Truncate(255), City = address.City.Truncate(255), State = address.State.Truncate(255), PostCode = address.PostCode.Truncate(11), Phone1 = contact.Mobile.Truncate(21), Phone2 = contact.Telephone.Truncate(21), Email = contact.Email.Truncate(255), ContactName = contact.Name.Truncate(25) }; } } public class CustomerMYOBPoster : IMYOBPoster { public CustomerMYOBPosterSettings Settings { get; set; } public MYOBGlobalPosterSettings GlobalSettings { get; set; } public MYOBConnectionData ConnectionData { get; set; } public bool BeforePost(IDataModel model) { foreach (var (_, table) in model.ModelTables) { table.IsDefault = false; } model.SetIsDefault(true); model.SetColumns(RequiredColumns()); return true; } public static Columns RequiredColumns() { return Columns.None() .Add(x => x.ID) .Add(x => x.PostedReference) .Add(x => x.DefaultContact.Name) .Add(x => x.Name) .Add(x => x.Code) .Add(x => x.CustomerStatus.ID) .Add(x => x.CustomerStatus.Active) .Add(x => x.Delivery.Street) .Add(x => x.Delivery.City) .Add(x => x.Delivery.State) .Add(x => x.Delivery.PostCode) .Add(x => x.Postal.Street) .Add(x => x.Postal.City) .Add(x => x.Postal.State) .Add(x => x.Postal.PostCode) .Add(x => x.DefaultContact.Mobile) .Add(x => x.DefaultContact.Telephone) .Add(x => x.DefaultContact.Email) .Add(x => x.DefaultContact.Name) .Add(x => x.DefaultContact.Mobile) .Add(x => x.ABN); } public static Result UpdateCustomer(MYOBConnectionData data, CustomerMYOBPosterSettings settings, Customer customer, MYOBCustomer myobCustomer, bool isNew) { // Documentation: https://developer.myob.com/api/myob-business-api/v2/contact/customer/ // Since this might be called from some other poster, we need to ensure we have the right columns. Client.EnsureColumns(customer, RequiredColumns()); ContactMYOBUtils.SplitName(customer.DefaultContact.Name, out var firstName, out var lastName); myobCustomer.CompanyName = customer.Name.Truncate(50); myobCustomer.FirstName = firstName.Truncate(30); myobCustomer.LastName = lastName.Truncate(20); myobCustomer.IsIndividual = false; myobCustomer.DisplayID = customer.Code.Truncate(15); // If there is not customer status, we will use default to Active = true. myobCustomer.IsActive = customer.CustomerStatus.ID == Guid.Empty || customer.CustomerStatus.Active; myobCustomer.Addresses = [ ContactMYOBUtils.ConvertAddress(customer.Delivery, 1, customer.DefaultContact), ContactMYOBUtils.ConvertAddress(customer.Postal, 2, customer.DefaultContact) ]; // Notes = // PhotoURI = // RowVersion = myobCustomer.SellingDetails.SaleLayout = InvoiceLayoutType.NoDefault; // myobCustomer.SellingDetails.PrintedFOrm = myobCustomer.SellingDetails.InvoiceDelivery = DocumentAction.PrintAndEmail; // myobCustomer.SellingDetails.IncomeAccount = // myobCustomer.SellingDetails.ReceiptMemo = // myobCustomer.SellingDetails.SalesPerson = // myobCustomer.SellingDetails.SaleComment = // myobCustomer.SellingDetails.ShippingMethod = // myobCustomer.SellingDetails.HourlyBillRate = // myobCustomer.SellingDetails.ABNBranch = myobCustomer.SellingDetails.ABN = customer.ABN.Truncate(14); if (isNew) { if (settings.DefaultTaxCode.IsNullOrWhiteSpace()) { throw new PostFailedMessageException("Default tax code has not been set up."); } else if(data.GetMYOBTaxCodeUID(settings.DefaultTaxCode).Get(out var taxID, out var error)) { if (taxID == Guid.Empty) { return Result.Error(new Exception($"Failed to find TaxCode in MYOB with code {settings.DefaultTaxCode}")); } myobCustomer.SellingDetails.TaxCode.UID = taxID; myobCustomer.SellingDetails.FreightTaxCode.UID = taxID; } else { CoreUtils.LogException("", error, $"Failed to find TaxCode in MYOB with code {settings.DefaultTaxCode}"); return Result.Error(new Exception($"Failed to find TaxCode in MYOB with code {settings.DefaultTaxCode}: {error.Message}", error)); } } return Result.Ok(); } /// /// Try to find a customer in MYOB which matches , and if this fails, create a new one. /// /// /// After this has finished, will be updated with set to the correct ID. ///
/// needs to have at least and as loaded columns. ///
/// /// The customer to map to. /// The UID of the MYOB customer. public static Result MapCustomer(MYOBConnectionData data, Customer customer) { if(Guid.TryParse(customer.PostedReference, out var myobID)) { return new Result(myobID); } var service = new CustomerService(data.Configuration, null, data.AuthKey); var result = service.Query(data, new Filter(x => x.DisplayID).IsEqualTo(customer.Code), top: 1); return result.MapOk(customers => { if(customers.Count == 0) { if(customer.Code.Length > 15) { return Result.Error(new Exception("Customer code is longer than 15 characters")); } var myobCustomer = new MYOBCustomer(); return UpdateCustomer(data, PosterUtils.LoadPosterSettings(), customer, myobCustomer, true) .MapOk>(() => { try { var result = service.UpdateEx(data.CompanyFile, myobCustomer, data.CompanyFileCredentials); customer.PostedReference = result.UID.ToString(); return Result.Ok(result.UID); } catch (Exception e) { CoreUtils.LogException("", e, $"Error while posting customer {customer.ID}"); return Result.Error(e); } }).Flatten(); } else { customer.PostedReference = customers.Items[0].UID.ToString(); return Result.Ok(customers.Items[0].UID); } }).Flatten(); } public IPostResult Process(IDataModel model) { var results = new PostResult(); var service = new CustomerService(ConnectionData.Configuration, null, ConnectionData.AuthKey); var customers = model.GetTable().ToArray(); foreach(var customer in customers) { if(customer.Code.Length > 15) { results.AddFailed(customer, "Code is longer than 15 characters."); continue; } bool isNew; MYOBCustomer myobCustomer; Exception? error; if(Guid.TryParse(customer.PostedReference, out var myobID)) { if(!service.Get(ConnectionData, myobID).Get(out var newCustomer, out error)) { CoreUtils.LogException("", error, $"Failed to find Customer in MYOB with id {myobID}"); results.AddFailed(customer, $"Failed to find Customer in MYOB with id {myobID}: {error.Message}"); continue; } myobCustomer = newCustomer; isNew = false; } else { myobCustomer = new MYOBCustomer(); isNew = true; } if(UpdateCustomer(ConnectionData, Settings, customer, myobCustomer, isNew).Get(out error)) { try { var result = service.UpdateEx(ConnectionData.CompanyFile, myobCustomer, ConnectionData.CompanyFileCredentials); customer.PostedReference = result.UID.ToString(); results.AddSuccess(customer); } catch(Exception e) { CoreUtils.LogException("", e, $"Error while posting customer {customer.ID}"); results.AddFailed(customer, e.Message); } } else { results.AddFailed(customer, error.Message); } } return results; } } public class CustomerMYOBPosterEngine : MYOBPosterEngine { }