|
|
@@ -0,0 +1,509 @@
|
|
|
+using Comal.Classes;
|
|
|
+using InABox.Clients;
|
|
|
+using InABox.Core;
|
|
|
+using InABox.Poster.Xero;
|
|
|
+using InABox.Scripting;
|
|
|
+using System;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.Linq;
|
|
|
+using System.Text;
|
|
|
+using System.Threading.Tasks;
|
|
|
+using XeroCustomer = Xero.NetStandard.OAuth2.Model.Accounting.Contact;
|
|
|
+using XeroAccounting = Xero.NetStandard.OAuth2.Model.Accounting;
|
|
|
+using PRS.Shared.Posters.Xero.Services;
|
|
|
+using InABox.Core.Postable;
|
|
|
+using InABox.Database;
|
|
|
+
|
|
|
+namespace PRS.Shared.Posters.Xero;
|
|
|
+
|
|
|
+public class CustomerXeroPosterSettings : XeroPosterSettings
|
|
|
+{
|
|
|
+ protected override string DefaultScript()
|
|
|
+ {
|
|
|
+ return @"using XeroCustomer = Xero.NetStandard.OAuth2.Model.Accounting.Contact;
|
|
|
+
|
|
|
+// 'Module' *must* implement " + nameof(ICustomerXeroPosterScript) + @"
|
|
|
+public class Module : " + nameof(ICustomerXeroPosterScript) + @"
|
|
|
+{
|
|
|
+ public void BeforePost(IDataModel<Customer> model)
|
|
|
+ {
|
|
|
+ // Perform pre-processing
|
|
|
+ }
|
|
|
+
|
|
|
+ public void ProcessCustomer(IDataModel<Customer> model, Customer customer, XeroCustomer xeroCustomer)
|
|
|
+ {
|
|
|
+ // Do extra processing for a customer; throw an exception to fail this customer.
|
|
|
+ }
|
|
|
+}";
|
|
|
+ }
|
|
|
+}
|
|
|
+public interface ICustomerXeroPosterScript
|
|
|
+{
|
|
|
+ void BeforePost(IDataModel<Customer> model);
|
|
|
+
|
|
|
+ void ProcessCustomer(IDataModel<Customer> model, Customer customer, XeroCustomer xeroCustomer);
|
|
|
+}
|
|
|
+
|
|
|
+public class CustomerXeroAutoRefresher : IAutoRefresher<Customer>
|
|
|
+{
|
|
|
+ public bool ShouldRepost(Customer customer)
|
|
|
+ {
|
|
|
+ var shouldRepost = customer.HasOriginalValue(x => x.Name)
|
|
|
+ || customer.HasOriginalValue(x => x.Code)
|
|
|
+ || customer.HasOriginalValue(x => x.ABN)
|
|
|
+ || customer.HasOriginalValue(x => x.Email)
|
|
|
+ || customer.HasOriginalValue(x => x.DefaultContact.ID)
|
|
|
+ // || customer.HasOriginalValue(x => x.Telephone)
|
|
|
+ // || customer.Delivery.HasOriginalValue(x => x.Street)
|
|
|
+ // || customer.Delivery.HasOriginalValue(x => x.City)
|
|
|
+ // || customer.Delivery.HasOriginalValue(x => x.State)
|
|
|
+ // || customer.Delivery.HasOriginalValue(x => x.PostCode)
|
|
|
+ || customer.Postal.HasOriginalValue(x => x.Street)
|
|
|
+ || customer.Postal.HasOriginalValue(x => x.City)
|
|
|
+ || customer.Postal.HasOriginalValue(x => x.State)
|
|
|
+ || customer.Postal.HasOriginalValue(x => x.PostCode);
|
|
|
+ if (shouldRepost)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(customer.CustomerStatus.HasOriginalValue(x => x.ID))
|
|
|
+ {
|
|
|
+ var originalID = customer.CustomerStatus.GetOriginalValue(x => x.ID);
|
|
|
+ var currentID = customer.CustomerStatus.ID;
|
|
|
+
|
|
|
+ var statuses = DbFactory.NewProvider(Logger.Main).Query(
|
|
|
+ Filter<CustomerStatus>.Where(x => x.ID).IsEqualTo(originalID).Or(x => x.ID).IsEqualTo(currentID),
|
|
|
+ Columns.None<CustomerStatus>().Add(x => x.ID).Add(x => x.Active))
|
|
|
+ .ToArray<CustomerStatus>();
|
|
|
+ if (statuses.Length == 2 && statuses[0].Active != statuses[1].Active)
|
|
|
+ {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+public class CustomerXeroPoster : IXeroPoster<Customer, CustomerXeroPosterSettings>, IAutoRefreshPoster<Customer, CustomerXeroAutoRefresher>
|
|
|
+{
|
|
|
+ public ScriptDocument? Script { get; set; }
|
|
|
+ public XeroConnectionData ConnectionData { get; set; }
|
|
|
+ public CustomerXeroPosterSettings Settings { get; set; }
|
|
|
+ public IPosterDispatcher Dispatcher { get; set; }
|
|
|
+
|
|
|
+ public ICustomerXeroPosterScript? ScriptObject => Script?.GetObject<ICustomerXeroPosterScript>();
|
|
|
+
|
|
|
+ public bool BeforePost(IDataModel<Customer> model)
|
|
|
+ {
|
|
|
+ foreach (var (_, table) in model.ModelTables)
|
|
|
+ {
|
|
|
+ table.IsDefault = false;
|
|
|
+ }
|
|
|
+ model.SetIsDefault<Customer>(true);
|
|
|
+ model.SetColumns<Customer>(RequiredColumns());
|
|
|
+
|
|
|
+ model.SetIsDefault<CustomerContact>(true, alias: "Customer_CustomerContact");
|
|
|
+ model.SetColumns<CustomerContact>(RequiredCustomerContactColumns(), alias: "Customer_CustomerContact");
|
|
|
+
|
|
|
+ ScriptObject?.BeforePost(model);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ #region Script Functions
|
|
|
+
|
|
|
+ private Result<Exception> ProcessCustomer(IDataModel<Customer> model, Customer customer, XeroCustomer xeroCustomer)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ ScriptObject?.ProcessCustomer(model, customer, xeroCustomer);
|
|
|
+ return Result.Ok();
|
|
|
+ }
|
|
|
+ catch(Exception e)
|
|
|
+ {
|
|
|
+ return Result.Error(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ public static Columns<Customer> RequiredColumns()
|
|
|
+ {
|
|
|
+ return Columns.None<Customer>()
|
|
|
+ .Add(x => x.ID)
|
|
|
+ .Add(x => x.PostedReference)
|
|
|
+ .Add(x => x.PostedStatus)
|
|
|
+ .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.Postal.Street)
|
|
|
+ .Add(x => x.Postal.City)
|
|
|
+ .Add(x => x.Postal.State)
|
|
|
+ .Add(x => x.Postal.PostCode)
|
|
|
+ .Add(x => x.Email)
|
|
|
+ // .Add(x => x.Telephone)
|
|
|
+ .Add(x => x.ABN);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static Columns<CustomerContact> RequiredCustomerContactColumns()
|
|
|
+ {
|
|
|
+ return Columns.None<CustomerContact>()
|
|
|
+ .Add(x => x.Customer.ID)
|
|
|
+ .Add(x => x.Contact.Name)
|
|
|
+ .Add(x => x.Contact.Email);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static Result<Exception> UpdateCustomer(XeroConnectionData data, Customer customer, List<CustomerContact> contacts, XeroCustomer xeroCustomer)
|
|
|
+ {
|
|
|
+ // Documentation: https://developer.xero.com/documentation/api/accounting/contacts
|
|
|
+
|
|
|
+ // Since this might be called from some other poster, we need to ensure we have the right columns.
|
|
|
+ Client.EnsureColumns(customer, RequiredColumns());
|
|
|
+
|
|
|
+ ContactXeroUtils.SplitName(customer.DefaultContact.Name, out var firstName, out var lastName);
|
|
|
+
|
|
|
+ // CompanyNumber
|
|
|
+ // BankAccountDetails
|
|
|
+ // AccountsReceivableTaxType
|
|
|
+ // AccountsPayableTaxType
|
|
|
+ // DefaultCurrency
|
|
|
+ // XeroNetworkKey
|
|
|
+ // SalesDefaultAccountCode
|
|
|
+ // PurchasesDefaultAccountCode
|
|
|
+ // SalesTrackingCategories
|
|
|
+ // PurchasesTrackingCategories
|
|
|
+ // TrackingCategoryName
|
|
|
+ // TrackingOptionName
|
|
|
+ // PaymentTerms
|
|
|
+
|
|
|
+ xeroCustomer.Name = customer.Name.Replace("<", "").Replace(">", "").Trim().Truncate(255).Trim();
|
|
|
+ xeroCustomer.FirstName = firstName;
|
|
|
+ xeroCustomer.LastName = lastName;
|
|
|
+ xeroCustomer.ContactNumber = customer.Code.Truncate(50);
|
|
|
+ xeroCustomer.AccountNumber = xeroCustomer.AccountNumber.NotWhiteSpaceOr(xeroCustomer.ContactNumber);
|
|
|
+ xeroCustomer.ContactStatus = customer.CustomerStatus.ID == Guid.Empty || customer.CustomerStatus.Active
|
|
|
+ ? XeroCustomer.ContactStatusEnum.ACTIVE
|
|
|
+ : XeroCustomer.ContactStatusEnum.ARCHIVED;
|
|
|
+ xeroCustomer.EmailAddress = customer.Email.Truncate(255);
|
|
|
+ xeroCustomer.ContactPersons = contacts.Select(x =>
|
|
|
+ {
|
|
|
+ var person = new XeroAccounting.ContactPerson();
|
|
|
+ ContactXeroUtils.SplitName(x.Contact.Name, out var firstName, out var lastName);
|
|
|
+ person.FirstName = firstName;
|
|
|
+ person.LastName = lastName;
|
|
|
+ person.EmailAddress = x.Contact.Email;
|
|
|
+ return person;
|
|
|
+ }).Take(5).ToList();
|
|
|
+ xeroCustomer.TaxNumber = customer.ABN;
|
|
|
+
|
|
|
+ xeroCustomer.Addresses =
|
|
|
+ [
|
|
|
+ ContactXeroUtils.ConvertAddress(customer.Postal, XeroAccounting.Address.AddressTypeEnum.POBOX),
|
|
|
+ // Xero says that DELIVERY addresses are not valid for contacts
|
|
|
+ // ContactXeroUtils.ConvertAddress(customer.Delivery, XeroAccounting.Address.AddressTypeEnum.DELIVERY)
|
|
|
+ ];
|
|
|
+ // xeroCustomer.Phones =
|
|
|
+ // [
|
|
|
+ // ContactXeroUtils.ConvertPhone(customer.Telephone, XeroAccounting.Phone.PhoneTypeEnum.OFFICE)
|
|
|
+ // ];
|
|
|
+
|
|
|
+ return Result.Ok();
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Try to find a customer in Xero which matches <paramref name="customer"/>, and if this fails, create a new one.
|
|
|
+ /// </summary>
|
|
|
+ /// <remarks>
|
|
|
+ /// After this has finished, <paramref name="customer"/> will be updated with <see cref="Customer.PostedReference"/> set to the correct ID.
|
|
|
+ /// <br/>
|
|
|
+ /// <paramref name="customer"/> needs to have at least <see cref="Customer.Code"/> and <see cref="Customer.PostedReference"/> as loaded columns.
|
|
|
+ /// </remarks>
|
|
|
+ /// <param name="data"></param>
|
|
|
+ /// <param name="customer">The customer to map to.</param>
|
|
|
+ /// <returns>The UID of the Xero customer.</returns>
|
|
|
+ public static Result<Guid, Exception> MapCustomer(XeroConnectionData data, Customer customer, List<CustomerContact> contacts)
|
|
|
+ {
|
|
|
+ if(Guid.TryParse(customer.PostedReference, out var myobID))
|
|
|
+ {
|
|
|
+ return Result.Ok(myobID);
|
|
|
+ }
|
|
|
+
|
|
|
+ var result = data.GetItem<ContactService, XeroCustomer>(Filter<XeroCustomer>.Where(x => x.ContactNumber).IsEqualTo(customer.Code));
|
|
|
+ return result.MapOk(xeroCustomer =>
|
|
|
+ {
|
|
|
+ if(xeroCustomer is null || !xeroCustomer.ContactID.HasValue)
|
|
|
+ {
|
|
|
+ if(customer.Code.Length > 50)
|
|
|
+ {
|
|
|
+ return Result.Error(new Exception("Customer code is longer than 50 characters"));
|
|
|
+ }
|
|
|
+ xeroCustomer = new XeroCustomer();
|
|
|
+ return UpdateCustomer(data, customer, contacts, xeroCustomer)
|
|
|
+ .MapOk(() => new ContactService(data).Save(xeroCustomer)
|
|
|
+ .MapOk(x =>
|
|
|
+ {
|
|
|
+ customer.PostedReference = x.ContactID.ToString() ?? "";
|
|
|
+ // Marking as repost because a script may not have run.
|
|
|
+ customer.PostedStatus = PostedStatus.RequiresRepost;
|
|
|
+ return x.ContactID ?? Guid.Empty;
|
|
|
+ }))
|
|
|
+ .Flatten();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ customer.PostedReference = xeroCustomer.ContactID.ToString() ?? "";
|
|
|
+ customer.PostedStatus = PostedStatus.RequiresRepost;
|
|
|
+ return Result.Ok(xeroCustomer.ContactID.Value);
|
|
|
+ }
|
|
|
+ }).Flatten();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static bool IsBlankCode(string code)
|
|
|
+ {
|
|
|
+ return code.IsNullOrWhiteSpace() || code.Equals("*None");
|
|
|
+ }
|
|
|
+
|
|
|
+ public IPullResult<Customer> Pull()
|
|
|
+ {
|
|
|
+ var result = new PullResult<Customer>();
|
|
|
+
|
|
|
+ var customerCodes = new HashSet<string>();
|
|
|
+
|
|
|
+ var page = 1;
|
|
|
+ var pageSize = 400;
|
|
|
+
|
|
|
+ var service = new ContactService(ConnectionData);
|
|
|
+ while (true)
|
|
|
+ {
|
|
|
+ if(!service.Query(null, page: page, pageSize: pageSize).Get(out var xeroCustomers, out var error))
|
|
|
+ {
|
|
|
+ CoreUtils.LogException("", error);
|
|
|
+ throw new PullFailedMessageException(error.Message);
|
|
|
+ }
|
|
|
+ if(xeroCustomers.Count == 0)
|
|
|
+ {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ var xeroIDs = xeroCustomers.ToArray(x => x.ContactID.ToString() ?? "");
|
|
|
+ var xeroCodes = xeroCustomers.Select(x => x.ContactNumber).Where(x => !IsBlankCode(x)).ToArray();
|
|
|
+ var xeroNames = xeroCustomers.Where(x => IsBlankCode(x.ContactNumber) && !x.Name.IsNullOrWhiteSpace())
|
|
|
+ .Select(x => x.Name).ToArray();
|
|
|
+
|
|
|
+ var customers = Client.Query(
|
|
|
+ Filter<Customer>.Where(x => x.PostedReference).InList(xeroIDs)
|
|
|
+ .Or(x => x.Code).InList(xeroCodes)
|
|
|
+ .Or(x => x.Name).InList(xeroNames),
|
|
|
+ Columns.None<Customer>().Add(x => x.ID).Add(x => x.PostedReference).Add(x => x.Code).Add(x => x.Name))
|
|
|
+ .ToArray<Customer>();
|
|
|
+ var customerDict = customers.Where(x => !x.PostedReference.IsNullOrWhiteSpace())
|
|
|
+ .ToDictionary(x => x.PostedReference);
|
|
|
+ var blankCustomers = customers.Where(x => x.PostedReference.IsNullOrWhiteSpace()).ToArray();
|
|
|
+
|
|
|
+ var needCodes = new Dictionary<string, (string prefix, int i, Customer customer)>();
|
|
|
+
|
|
|
+ foreach(var xeroCustomer in xeroCustomers)
|
|
|
+ {
|
|
|
+ if (customerDict.TryGetValue(xeroCustomer.ContactID.ToString() ?? "", out var customer))
|
|
|
+ {
|
|
|
+ // Skipping existing customers at this point.
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ customer = !IsBlankCode(xeroCustomer.ContactNumber)
|
|
|
+ ? blankCustomers.FirstOrDefault(x => string.Equals(x.Code, xeroCustomer.ContactNumber))
|
|
|
+ : blankCustomers.FirstOrDefault(x => string.Equals(x.Name, xeroCustomer.Name));
|
|
|
+ if(customer is not null)
|
|
|
+ {
|
|
|
+ customer.PostedReference = xeroCustomer.ContactID.ToString() ?? "";
|
|
|
+ result.AddEntity(PullResultType.Linked, customer);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ customer = new Customer();
|
|
|
+
|
|
|
+ string code;
|
|
|
+ if (!IsBlankCode(xeroCustomer.ContactNumber))
|
|
|
+ {
|
|
|
+ code = xeroCustomer.ContactID.ToString() ?? "";
|
|
|
+ }
|
|
|
+ else if (!xeroCustomer.Name.IsNullOrWhiteSpace())
|
|
|
+ {
|
|
|
+ code = xeroCustomer.Name[..Math.Min(3, xeroCustomer.Name.Length)].ToUpper();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ code = "CUS";
|
|
|
+ }
|
|
|
+ int i = 1;
|
|
|
+ customer.Code = code;
|
|
|
+ while (customerCodes.Contains(customer.Code))
|
|
|
+ {
|
|
|
+ customer.Code = $"{code}{i:d3}";
|
|
|
+ ++i;
|
|
|
+ }
|
|
|
+ customerCodes.Add(customer.Code);
|
|
|
+
|
|
|
+ customer.Name = xeroCustomer.Name;
|
|
|
+ customer.ABN = xeroCustomer.TaxNumber;
|
|
|
+ customer.Email = xeroCustomer.EmailAddress;
|
|
|
+
|
|
|
+ if(xeroCustomer.Addresses is not null)
|
|
|
+ {
|
|
|
+ var postal = xeroCustomer.Addresses.FirstOrDefault(x => x.AddressType == XeroAccounting.Address.AddressTypeEnum.POBOX);
|
|
|
+ if(postal is not null)
|
|
|
+ {
|
|
|
+ customer.Postal.CopyFrom(ContactXeroUtils.ConvertAddress(postal));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach(var contactPerson in xeroCustomer.ContactPersons)
|
|
|
+ {
|
|
|
+ var contact = new Contact();
|
|
|
+ contact.Name = $"{contactPerson.FirstName} {contactPerson.LastName}";
|
|
|
+ contact.Email = contactPerson.EmailAddress;
|
|
|
+
|
|
|
+ result.AddChildEntity(customer, contact);
|
|
|
+ result
|
|
|
+ .AddChildEntity(customer, new CustomerContact(),
|
|
|
+ (p, c) => c.Customer.ID = p.ID)
|
|
|
+ .AddParent(contact,
|
|
|
+ (p, c) => c.Contact.ID = p.ID);
|
|
|
+ }
|
|
|
+
|
|
|
+ // customer.Telephone = xeroCustomer.Phones.FirstOrDefault()?.PhoneNumber ?? "";
|
|
|
+
|
|
|
+ customer.PostedReference = xeroCustomer.ContactID.ToString() ?? "";
|
|
|
+ result.AddEntity(PullResultType.New, customer);
|
|
|
+ needCodes.Add(customer.Code, (code, i, customer));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Do code clash checking
|
|
|
+ while(needCodes.Count > 0)
|
|
|
+ {
|
|
|
+ var codes = Client.Query(
|
|
|
+ Filter<Customer>.Where(x => x.Code).InList(needCodes.Values.ToArray(x => x.customer.Code)),
|
|
|
+ Columns.None<Customer>().Add(x => x.Code));
|
|
|
+ var newNeedCodes = new Dictionary<string, (string prefix, int i, Customer customer)>();
|
|
|
+ foreach(var row in codes.Rows)
|
|
|
+ {
|
|
|
+ var code = row.Get<Customer, string>(x => x.Code);
|
|
|
+ if(needCodes.Remove(code, out var needed))
|
|
|
+ {
|
|
|
+ int i = needed.i;
|
|
|
+ do
|
|
|
+ {
|
|
|
+ needed.customer.Code = $"{needed.prefix}{i:d3}";
|
|
|
+ ++i;
|
|
|
+ } while (customerCodes.Contains(needed.customer.Code));
|
|
|
+ customerCodes.Add(needed.customer.Code);
|
|
|
+ newNeedCodes.Add(needed.customer.Code, (needed.prefix, i, needed.customer));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ needCodes = newNeedCodes;
|
|
|
+ }
|
|
|
+
|
|
|
+ ++page;
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ public IPostResult<Customer> Process(IDataModel<Customer> model)
|
|
|
+ {
|
|
|
+ var results = new PostResult<Customer>();
|
|
|
+
|
|
|
+ var service = new ContactService(ConnectionData);
|
|
|
+
|
|
|
+ var customers = model.GetTable<Customer>().ToArray<Customer>();
|
|
|
+ var contacts = model.GetTable<CustomerContact>(alias: "Customer_CustomerContact")
|
|
|
+ .ToObjects<CustomerContact>()
|
|
|
+ .GroupByDictionary(x => x.Customer.ID);
|
|
|
+
|
|
|
+ foreach(var customer in customers)
|
|
|
+ {
|
|
|
+ if(customer.Code.Length > 50)
|
|
|
+ {
|
|
|
+ results.AddFailed(customer, "Code is longer than 50 characters.");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ var customerContacts = contacts.GetValueOrDefault(customer.ID) ?? [];
|
|
|
+
|
|
|
+ XeroCustomer xeroCustomer;
|
|
|
+ Exception? error;
|
|
|
+ if(Guid.TryParse(customer.PostedReference, out var xeroID))
|
|
|
+ {
|
|
|
+ if(!service.Get(xeroID).Get(out var newCustomer, out error))
|
|
|
+ {
|
|
|
+ CoreUtils.LogException("", error, $"Failed to find Customer in Xero with id {xeroID}");
|
|
|
+ results.AddFailed(customer, $"Failed to find Customer in Xero with id {xeroID}: {error.Message}");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ xeroCustomer = newCustomer;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ if (service.GetItem(Filter<XeroCustomer>.Where(x => x.ContactNumber).IsEqualTo(customer.Code))
|
|
|
+ .Get(out var externalXeroCustomer, out error)
|
|
|
+ && externalXeroCustomer is not null)
|
|
|
+ {
|
|
|
+ xeroCustomer = externalXeroCustomer;
|
|
|
+ }
|
|
|
+ else if (service.GetItem(
|
|
|
+ Filter<XeroCustomer>.Where(x => x.Name).IsEqualTo(customer.Name)
|
|
|
+ .And(Filter<XeroCustomer>.Where(x => x.ContactNumber).IsEqualTo(null)
|
|
|
+ .Or(x => x.ContactNumber).IsEqualTo("")
|
|
|
+ .Or(x => x.ContactNumber).IsEqualTo("*None")))
|
|
|
+ .Get(out externalXeroCustomer, out error)
|
|
|
+ && externalXeroCustomer is not null)
|
|
|
+ {
|
|
|
+ xeroCustomer = externalXeroCustomer;
|
|
|
+ xeroCustomer.ContactNumber = customer.Code;
|
|
|
+ }
|
|
|
+ else if(error is null)
|
|
|
+ {
|
|
|
+ xeroCustomer = new();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ CoreUtils.LogException("", error);
|
|
|
+ results.AddFailed(customer, error.Message);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if(UpdateCustomer(ConnectionData, customer, customerContacts, xeroCustomer)
|
|
|
+ .MapOk(() => ProcessCustomer(model, customer, xeroCustomer)).Flatten()
|
|
|
+ .MapOk(() => service.Save(xeroCustomer)).Flatten()
|
|
|
+ .Get(out var result, out error))
|
|
|
+ {
|
|
|
+ customer.PostedReference = result.ContactID.ToString() ?? "";
|
|
|
+ results.AddSuccess(customer);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ CoreUtils.LogException("", error, $"Error while posting customer {customer.ID}");
|
|
|
+ results.AddFailed(customer, error.Message);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return results;
|
|
|
+ }
|
|
|
+}
|
|
|
+public class CustomerMYOBPosterEngine<T> : XeroPosterEngine<Customer, CustomerXeroPoster, CustomerXeroPosterSettings>, IPullerEngine<Customer, CustomerXeroPoster>
|
|
|
+{
|
|
|
+ public IPullResult<Customer> DoPull()
|
|
|
+ {
|
|
|
+ LoadConnectionData();
|
|
|
+ return Poster.Pull();
|
|
|
+ }
|
|
|
+
|
|
|
+ protected override IList<string> RequiredScopes()
|
|
|
+ {
|
|
|
+ return [
|
|
|
+ "accounting.contacts"
|
|
|
+ ];
|
|
|
+ }
|
|
|
+}
|