Advertisement
Virajsinh

Laravel 11 Xero API Service

May 10th, 2025 (edited)
230
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 13.57 KB | Source Code | 0 0
  1. <?php
  2.  
  3. namespace App\Providers;
  4.  
  5. use XeroAPI\XeroPHP\Api\AccountingApi;
  6. use XeroAPI\XeroPHP\Models\Accounting\Invoice;
  7. use XeroAPI\XeroPHP\Models\Accounting\Invoices;
  8. use XeroAPI\XeroPHP\Models\Accounting\Contact;
  9. use XeroAPI\XeroPHP\Models\Accounting\Contacts;
  10. use XeroAPI\XeroPHP\Models\Accounting\Email;
  11. use XeroAPI\XeroPHP\Models\Accounting\LineItem;
  12. use XeroAPI\XeroPHP\Models\Accounting\Accounts;
  13. use XeroAPI\XeroPHP\Models\Accounting\InventoryItem;
  14. use XeroAPI\XeroPHP\Models\Accounting\Payment;
  15. use XeroAPI\XeroPHP\Models\Accounting\Payments;
  16. use XeroAPI\XeroPHP\Models\Accounting\Account;
  17. use Illuminate\Support\Facades\Log;
  18. use Exception;
  19.  
  20. class XeroService2
  21. {
  22.     protected $xero;
  23.     protected $tenantId;
  24.  
  25.     /**
  26.      * Constructor
  27.      *
  28.      * @param AccountingApi $xero     Injected instance of Xero Accounting API
  29.      * @param string        $tenantId Xero Tenant ID
  30.      */
  31.     public function __construct(AccountingApi $xero, $tenantId)
  32.     {
  33.         $this->xero = $xero;
  34.         $this->tenantId = $tenantId;
  35.     }
  36.  
  37.     /**
  38.      * Create and send an invoice to Xero.
  39.      *
  40.      * @param Contact      $contact       The Xero contact object
  41.      * @param LineItem[]   $lineItems     Array of line items
  42.      * @param string|null  $invoiceNumber Optional invoice number
  43.      * @param string|null  $reference     Optional reference
  44.      * @param array|null   $terms         Optional terms array
  45.      * @param string|null  $dueDate       Optional due date (Y-m-d)
  46.      * @return Invoice
  47.      * @throws Exception
  48.      */
  49.     public function createInvoice($contact, $lineItems, $invoiceNumber = null, $reference = null, $terms = null, $dueDate = null)
  50.     {
  51.         try {
  52.             $invoice = new Invoice([
  53.                 'type'           => Invoice::TYPE_ACCREC,
  54.                 'contact'        => $contact,
  55.                 'line_items'     => $lineItems,
  56.                 'date'           => now()->format('Y-m-d'),
  57.                 'due_date'       => $dueDate ?: now()->addDays(14)->format('Y-m-d'),
  58.                 'status'         => Invoice::STATUS_AUTHORISED,
  59.                 'invoice_number' => $invoiceNumber,
  60.                 'reference'      => $reference,
  61.                 'terms'          => $terms,
  62.             ]);
  63.  
  64.             $invoices = new Invoices();
  65.             $invoices->setInvoices([$invoice]);
  66.  
  67.             $response = $this->xero->createInvoices($this->tenantId, $invoices);
  68.             return $response->getInvoices()[0];
  69.         } catch (Exception $e) {
  70.             Log::error('Invoice creation failed', ['error' => $e->getMessage()]);
  71.             throw $e;
  72.         }
  73.     }
  74.  
  75.     /**
  76.      * Update the status of an invoice (e.g., AUTHORIZED, VOIDED).
  77.      *
  78.      * @param string $invoiceId UUID of the invoice
  79.      * @param string $status    New status
  80.      * @return Invoice
  81.      * @throws Exception
  82.      */
  83.     public function updateInvoiceStatus($invoiceId, $status)
  84.     {
  85.         try {
  86.             $invoice = new Invoice(['status' => $status]);
  87.             return $this->xero->updateInvoice($this->tenantId, $invoiceId, $invoice);
  88.         } catch (Exception $e) {
  89.             Log::error('Update invoice status failed', ['error' => $e->getMessage()]);
  90.             throw $e;
  91.         }
  92.     }
  93.  
  94.     /**
  95.      * Update the reference field on an invoice.
  96.      *
  97.      * @param string      $invoiceId UUID of the invoice
  98.      * @param string|null $reference Reference text
  99.      * @return Invoice
  100.      * @throws Exception
  101.      */
  102.     public function updateInvoiceReference($invoiceId, $reference = null)
  103.     {
  104.         try {
  105.             $invoice = new Invoice(['reference' => $reference]);
  106.             return $this->xero->updateInvoice($this->tenantId, $invoiceId, $invoice);
  107.         } catch (Exception $e) {
  108.             Log::error('Update invoice reference failed', ['error' => $e->getMessage()]);
  109.             throw $e;
  110.         }
  111.     }
  112.  
  113.     /**
  114.      * Fetch a single invoice by its ID.
  115.      *
  116.      * @param string $invoiceId UUID of the invoice
  117.      * @return Invoice
  118.      * @throws Exception
  119.      */
  120.     public function getInvoiceById($invoiceId)
  121.     {
  122.         try {
  123.             return $this->xero->getInvoice($this->tenantId, $invoiceId)->getInvoices()[0];
  124.         } catch (Exception $e) {
  125.             Log::error('Fetch invoice failed', ['error' => $e->getMessage()]);
  126.             throw $e;
  127.         }
  128.     }
  129.  
  130.     /**
  131.      * Fetch all invoices for a specific contact ID.
  132.      *
  133.      * @param string $contactId UUID of the contact
  134.      * @return Invoice[]
  135.      * @throws Exception
  136.      */
  137.     public function getInvoicesByContactId($contactId)
  138.     {
  139.         try {
  140.             $response = $this->xero->getInvoices($this->tenantId, null, 'Contact.ContactID=="' . $contactId . '"');
  141.             return $response->getInvoices();
  142.         } catch (Exception $e) {
  143.             Log::error('Fetch invoices by contact failed', ['error' => $e->getMessage()]);
  144.             throw $e;
  145.         }
  146.     }
  147.  
  148.     /**
  149.      * Send an invoice to the contact's email.
  150.      *
  151.      * @param string $invoiceId UUID of the invoice
  152.      * @return mixed
  153.      * @throws Exception
  154.      */
  155.     public function emailInvoice($invoiceId)
  156.     {
  157.         try {
  158.             return $this->xero->emailInvoice($this->tenantId, $invoiceId);
  159.         } catch (Exception $e) {
  160.             Log::error('Send invoice email failed', ['error' => $e->getMessage()]);
  161.             throw $e;
  162.         }
  163.     }
  164.  
  165.     /**
  166.      * Create or update a Xero contact.
  167.      *
  168.      * @param string|null $contactId  Existing contact ID to update
  169.      * @param string|null $name       Contact name
  170.      * @param string|null $email      Email address
  171.      * @param string|null $phone      Phone number
  172.      * @param string|null $firstName  First name
  173.      * @param string|null $lastName   Last name
  174.      * @return Contact
  175.      * @throws Exception
  176.      */
  177.     public function upsertContact($contactId = null, $name = null, $email = null, $phone = null, $firstName = null, $lastName = null)
  178.     {
  179.         try {
  180.             $contact = new Contact([
  181.                 'contact_id'     => $contactId,
  182.                 'name'           => $name,
  183.                 'email_address'  => $email,
  184.                 'first_name'     => $firstName,
  185.                 'last_name'      => $lastName,
  186.                 'phones'         => $phone ? [['phone_number' => $phone]] : [],
  187.             ]);
  188.  
  189.             $contacts = new Contacts();
  190.             $contacts->setContacts([$contact]);
  191.  
  192.             $response = $this->xero->createContacts($this->tenantId, $contacts);
  193.             return $response->getContacts()[0];
  194.         } catch (Exception $e) {
  195.             Log::error('Upsert contact failed', ['error' => $e->getMessage()]);
  196.             throw $e;
  197.         }
  198.     }
  199.  
  200.     /**
  201.      * Update contact status (e.g., ARCHIVED, ACTIVE).
  202.      *
  203.      * @param string $contactId UUID of the contact
  204.      * @param string $status    New status
  205.      * @return Contact
  206.      * @throws Exception
  207.      */
  208.     public function updateContactStatus($contactId, $status)
  209.     {
  210.         try {
  211.             $contact = new Contact(['contact_status' => $status]);
  212.             return $this->xero->updateContact($this->tenantId, $contactId, $contact);
  213.         } catch (Exception $e) {
  214.             Log::error('Update contact status failed', ['error' => $e->getMessage()]);
  215.             throw $e;
  216.         }
  217.     }
  218.  
  219.     /**
  220.      * Get a contact by ID.
  221.      *
  222.      * @param string $contactId UUID of the contact
  223.      * @return Contact
  224.      * @throws Exception
  225.      */
  226.     public function getContactById($contactId)
  227.     {
  228.         try {
  229.             return $this->xero->getContact($this->tenantId, $contactId)->getContacts()[0];
  230.         } catch (Exception $e) {
  231.             Log::error('Get contact failed', ['error' => $e->getMessage()]);
  232.             throw $e;
  233.         }
  234.     }
  235.  
  236.     /**
  237.      * Find contact by email.
  238.      *
  239.      * @param string $email
  240.      * @return Contact[]
  241.      * @throws Exception
  242.      */
  243.     public function findContactByEmail($email)
  244.     {
  245.         return $this->findContactByField('EmailAddress', $email);
  246.     }
  247.  
  248.     /**
  249.      * Find contact by name.
  250.      *
  251.      * @param string $name
  252.      * @return Contact[]
  253.      * @throws Exception
  254.      */
  255.     public function findContactByName($name)
  256.     {
  257.         return $this->findContactByField('Name', $name);
  258.     }
  259.  
  260.     /**
  261.      * Find contact by phone number.
  262.      *
  263.      * @param string $phone
  264.      * @return Contact[]
  265.      * @throws Exception
  266.      */
  267.     public function findContactByPhone($phone)
  268.     {
  269.         return $this->findContactByField('Phones.PhoneNumber', $phone);
  270.     }
  271.  
  272.     /**
  273.      * Generic helper to find a contact by any field.
  274.      *
  275.      * @param string $field
  276.      * @param string $value
  277.      * @return Contact[]
  278.      * @throws Exception
  279.      */
  280.     protected function findContactByField($field, $value)
  281.     {
  282.         try {
  283.             $where = "$field==\"$value\"";
  284.             $response = $this->xero->getContacts($this->tenantId, null, $where);
  285.             return $response->getContacts();
  286.         } catch (Exception $e) {
  287.             Log::error("Search contact by $field failed", ['error' => $e->getMessage()]);
  288.             throw $e;
  289.         }
  290.     }
  291.  
  292.     /**
  293.      * Get all chart of accounts from Xero.
  294.      *
  295.      * @return Accounts[]
  296.      * @throws Exception
  297.      */
  298.     public function getAccountsList()
  299.     {
  300.         try {
  301.             return $this->xero->getAccounts($this->tenantId)->getAccounts();
  302.         } catch (Exception $e) {
  303.             Log::error('Get accounts list failed', ['error' => $e->getMessage()]);
  304.             throw $e;
  305.         }
  306.     }
  307.  
  308.     /**
  309.      * Fetch all inventory items from Xero
  310.      *
  311.      * @param string $xeroTenantId The Xero tenant ID for the authenticated account.
  312.      *
  313.      * @return array An array of inventory items from Xero.
  314.      * @throws \XeroAPI\XeroPHP\ApiException If the API request fails.
  315.      */
  316.     public function getAllInventoryItems()
  317.     {
  318.         try {
  319.             // Make API call to get all inventory items
  320.             $items = $this->xero->getItems($this->tenantId);
  321.  
  322.             // Return the retrieved inventory items
  323.             return $items;
  324.         } catch (\XeroAPI\XeroPHP\ApiException $e) {
  325.             // Log or handle the exception as needed
  326.             // For now, we simply rethrow it
  327.             throw new \Exception("Error fetching inventory items: " . $e->getMessage());
  328.         }
  329.     }
  330.  
  331.     /**
  332.      * Create or update an inventory item.
  333.      *
  334.      * @param string      $code       Unique item code
  335.      * @param string      $name       Item name
  336.      * @param string|null $description Description
  337.      * @param float|null  $unitPrice  Sale and purchase price
  338.      * @param string|null $inventoryAssetAccountCode Inventory account code
  339.      * @param bool        $isTracked  Whether item is tracked in inventory
  340.      * @return InventoryItem
  341.      * @throws Exception
  342.      */
  343.     public function upsertInventoryItem($code, $name, $description = null, $unitPrice = null, $inventoryAssetAccountCode = null, $isTracked = false)
  344.     {
  345.         try {
  346.             $item = new InventoryItem([
  347.                 'code'                         => $code,
  348.                 'name'                         => $name,
  349.                 'description'                  => $description,
  350.                 'purchase_details'             => ['unit_price' => $unitPrice],
  351.                 'sales_details'                => ['unit_price' => $unitPrice],
  352.                 'inventory_asset_account_code' => $inventoryAssetAccountCode,
  353.                 'is_tracked_as_inventory'      => $isTracked,
  354.             ]);
  355.  
  356.             return $this->xero->createInventoryItem($this->tenantId, $item);
  357.         } catch (Exception $e) {
  358.             Log::error('Upsert inventory item failed', ['error' => $e->getMessage()]);
  359.             throw $e;
  360.         }
  361.     }
  362.  
  363.     /**
  364.      * Delete an inventory item by ID.
  365.      *
  366.      * @param string $itemId UUID of the item
  367.      * @return mixed
  368.      * @throws Exception
  369.      */
  370.     public function deleteInventoryItem($itemId)
  371.     {
  372.         try {
  373.             return $this->xero->deleteInventoryItem($this->tenantId, $itemId);
  374.         } catch (Exception $e) {
  375.             Log::error('Delete inventory item failed', ['error' => $e->getMessage()]);
  376.             throw $e;
  377.         }
  378.     }
  379.  
  380.     /**
  381.      * Find inventory by code.
  382.      *
  383.      * @param string $code
  384.      * @return InventoryItem[]
  385.      * @throws Exception
  386.      */
  387.     public function getInventoryByCode($code)
  388.     {
  389.         return $this->findInventoryByField('Code', $code);
  390.     }
  391.  
  392.     /**
  393.      * Find inventory by name.
  394.      *
  395.      * @param string $name
  396.      * @return InventoryItem[]
  397.      * @throws Exception
  398.      */
  399.     public function getInventoryByName($name)
  400.     {
  401.         return $this->findInventoryByField('Name', $name);
  402.     }
  403.  
  404.     /**
  405.      * Generic helper to find an inventory item by field.
  406.      *
  407.      * @param string $field
  408.      * @param string $value
  409.      * @return InventoryItem[]
  410.      * @throws Exception
  411.      */
  412.     protected function findInventoryByField($field, $value)
  413.     {
  414.         try {
  415.             $where = "$field==\"$value\"";
  416.             $response = $this->xero->getItems($this->tenantId, null, $where);
  417.             return $response->getItems();
  418.         } catch (Exception $e) {
  419.             Log::error("Search inventory by $field failed", ['error' => $e->getMessage()]);
  420.             throw $e;
  421.         }
  422.     }
  423.  
  424.     /**
  425.      * Create a payment in Xero for a specific invoice using either account code or account ID.
  426.      *
  427.      * @param string $invoiceId        The Xero Invoice ID (UUID) for which payment is being made.
  428.      * @param float  $amount           The amount to be paid.
  429.      * @param string $accountCodeOrId The account code (e.g., '090') or account ID (UUID) of the bank account.
  430.      *
  431.      * @return \XeroAPI\XeroPHP\Models\Accounting\Payment The created payment object from Xero.
  432.      *
  433.      * @throws \Exception If the payment creation fails or an error occurs in the API call.
  434.      */
  435.     public function createPayment(string $invoiceId, float $amount, string $accountCodeOrId)
  436.     {
  437.         try {
  438.             // Determine if the provided account identifier is a UUID (account ID) or a code
  439.             $isUuid = preg_match('/^[0-9a-fA-F-]{36}$/', $accountCodeOrId);
  440.  
  441.             // Create Account object using either account_id or code
  442.             $account = $isUuid ? new Account(['account_id' => $accountCodeOrId]): new Account(['code' => $accountCodeOrId]);
  443.  
  444.             // Prepare the Payment object
  445.             $payment = new Payment([
  446.                 'invoice' => new Invoice(['invoice_id' => $invoiceId]),    // Link to the invoice
  447.                 'account' => $account,                                     // Specify the payment account
  448.                 'date'    => now()->format('Y-m-d'),                       // Use today's date for payment
  449.                 'amount'  => $amount,                                      // Set the payment amount
  450.             ]);
  451.  
  452.             // Wrap the payment in a Payments collection
  453.             $payments = new Payments();
  454.             $payments->setPayments([$payment]);
  455.  
  456.             // Send the payment to Xero
  457.             $response = $this->xero->createPayments($this->tenantId, $payments);
  458.  
  459.             // Return the created payment from the response
  460.             return $response->getPayments()[0];
  461.         } catch (Exception $e) {
  462.             // Log and rethrow the exception in case of failure
  463.             Log::error('Create payment failed', ['error' => $e->getMessage()]);
  464.             throw $e;
  465.         }
  466.     }
  467.  
  468. }
  469.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement