Include customer user data in the collected order invoices payload by retrieving user information based on customer number. This ensures that relevant user details are available for further processing or integration.
646 lines
23 KiB
PHP
646 lines
23 KiB
PHP
<?php
|
|
|
|
namespace objects;
|
|
|
|
use classes\db;
|
|
use classes\economic;
|
|
use classes\object_property;
|
|
use Exception;
|
|
use traits\db_object_t;
|
|
|
|
class collected_order_invoices_o extends db
|
|
{
|
|
use db_object_t;
|
|
|
|
public object_property $customer_number;
|
|
public object_property $name;
|
|
public object_property $notes;
|
|
public object_property $processor;
|
|
public object_property $external_id;
|
|
|
|
public object_property $booked_invoice_id;
|
|
public object_property $created_at;
|
|
public object_property $updated_at;
|
|
public object_property $closed_at;
|
|
public array $processors = [
|
|
1 => 'E-conomic',
|
|
2 => 'Stripe',
|
|
];
|
|
|
|
public function structure(): void
|
|
{
|
|
$this->setTable('collected_order_invoices');
|
|
}
|
|
|
|
/**
|
|
* List all collected order invoices for a customer
|
|
* @param int $customer_number The E-conomic customer number
|
|
* @return array The list of collected order invoices
|
|
* @throws Exception If the request was not successful
|
|
*/
|
|
public function getCustomerInvoiceCollections(int $customer_number): array
|
|
{
|
|
$collections = self::getFieldsWhere(
|
|
[
|
|
'customer_number' => $customer_number,
|
|
'deleted_at' => null
|
|
],
|
|
['id', 'name', 'notes', 'processor', 'external_id', 'created_at', 'updated_at', 'closed_at']
|
|
);
|
|
// Parse the results
|
|
$result = [];
|
|
foreach ( $collections as $collection ) {
|
|
$this->id = $collection['id'];
|
|
self::getObjectProperties();
|
|
self::requireSelected();
|
|
$result[] = self::asArray();
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
public function getObjectProperties(): void
|
|
{
|
|
$this->customer_number = new object_property($this->table, $this->id, 'customer_number', 'int', false);
|
|
$this->name = new object_property($this->table, $this->id, 'name', 'string', false);
|
|
$this->notes = new object_property($this->table, $this->id, 'notes', 'string', false);
|
|
$this->processor = new object_property($this->table, $this->id, 'processor', 'int', false);
|
|
$this->external_id = new object_property($this->table, $this->id, 'external_id', 'string', false);
|
|
$this->created_at = new object_property($this->table, $this->id, 'created_at', 'string', false);
|
|
$this->updated_at = new object_property($this->table, $this->id, 'updated_at', 'string', false);
|
|
$this->closed_at = new object_property($this->table, $this->id, 'closed_at', 'string', false);
|
|
$this->booked_invoice_id = new object_property($this->table, $this->id, 'booked_invoice_id', 'int', false);
|
|
}
|
|
|
|
public function asArray(): array
|
|
{
|
|
$tmp = [
|
|
'id' => (int)$this->id,
|
|
'customer_number' => (int)$this->customer_number->value(),
|
|
'name' => (string)$this->name->value(),
|
|
'notes' => (string)$this->notes->value(),
|
|
'processor' => (int)$this->processor->value(),
|
|
'external_id' => (string)$this->external_id->value(),
|
|
'created_at' => (string)$this->created_at->value(),
|
|
'updated_at' => (string)$this->updated_at->value(),
|
|
'closed_at' => (string)$this->closed_at->value(),
|
|
'orders' => $this->getOrders(),
|
|
'economic_invoice_draft_id' => null, // This will be overwritten if the external id is set
|
|
'economic_invoice_booked_id' => null, // This will be overwritten if the external id is set
|
|
'total_net_amount' => self::getTotalAmount(),
|
|
'user' => (array)(new users_o())->getUserByCustomerNumber($this->customer_number->value())->asArray(),
|
|
];
|
|
// If the external id is set, get the invoice draft id
|
|
if (!empty($this->external_id->value())) {
|
|
$tmp['economic_invoice_draft_id'] = self::isDraftExisting() ? self::getInvoiceDraftId() : null;
|
|
$tmp['economic_invoice_booked_id'] = self::isBooked() ? self::getInvoiceBookedId() : null;
|
|
}
|
|
return $tmp;
|
|
}
|
|
|
|
/**
|
|
* Get the orders in the invoice collection
|
|
* @param bool $count If the count of orders should be returned instead of the orders
|
|
* @return array|int The list of orders in the invoice collection, or the count of orders if $count is true
|
|
* @throws Exception If the request was not successful
|
|
*/
|
|
public function getOrders(bool $count = false): array|int
|
|
{
|
|
$orders = new orders_o();
|
|
$order_ids = $orders->getFieldsWhere(
|
|
[
|
|
'invoice_collection_id' => $this->id,
|
|
'deleted_at' => null
|
|
],
|
|
['id']
|
|
);
|
|
if ($count) {
|
|
return count($order_ids);
|
|
}
|
|
$result = [];
|
|
foreach ( $order_ids as $order_id ) {
|
|
$orders->select($order_id['id']);
|
|
$orders->requireSelected();
|
|
$result[] = $orders->asArray();
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Get the net amount (sum) of the invoice collection
|
|
* @return float The total amount of the invoice collection
|
|
* @throws Exception If the request was not successful
|
|
* @throws Exception If the invoice collection is not set
|
|
*/
|
|
public function getTotalAmount(): float
|
|
{
|
|
// Require the invoice collection to be selected
|
|
self::requireSelected();
|
|
// Get the orders in the invoice collection
|
|
$order_ids = self::getOrderIds();
|
|
// Get the orders in the invoice collection
|
|
$net_amount = 0;
|
|
|
|
foreach ( $order_ids as $order_id ) {
|
|
$order = new orders_o();
|
|
$order->select($order_id['id']);
|
|
$order->requireSelected();
|
|
// Get the net amount of the order
|
|
$net_amount += $order->getNetAmount();
|
|
}
|
|
return $net_amount;
|
|
}
|
|
|
|
/**
|
|
* Get the order ids in the invoice collection
|
|
* @return array The list of order ids in the invoice collection
|
|
* @throws Exception If the request was not successful
|
|
*/
|
|
public function getOrderIds(): array
|
|
{
|
|
// Require the invoice collection to be selected
|
|
self::requireSelected();
|
|
// Get the orders in the invoice collection
|
|
$orders_o = new orders_o();
|
|
return $orders_o->getFieldsWhere(
|
|
[
|
|
'invoice_collection_id' => $this->id,
|
|
'deleted_at' => null
|
|
],
|
|
['id']
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Check if the invoice draft is existing in E-conomic
|
|
* @returns bool If the invoice draft is existing
|
|
* @throws Exception If the request was not successful
|
|
* @throws Exception If the invoice collection is not set
|
|
*/
|
|
public function isDraftExisting(): bool
|
|
{
|
|
// Require the invoice collection to be selected
|
|
self::requireSelected();
|
|
// If the external id is empty, the invoice draft does not exist
|
|
if ($this->external_id->value() === null) {
|
|
return false;
|
|
}
|
|
// Get the invoice draft id from the external id
|
|
try {
|
|
self::getInvoiceDraftId();
|
|
return true;
|
|
} catch (Exception $e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the invoice draft id from the external id
|
|
* @return int The invoice draft id
|
|
* @throws Exception If the request was not successful
|
|
* @throws Exception If the invoice draft was not found
|
|
*/
|
|
public function getInvoiceDraftId(): int
|
|
{
|
|
// Require the invoice collection to be selected
|
|
self::requireSelected();
|
|
// Check if the external id is set
|
|
if ($this->external_id->value() === null) {
|
|
throw new Exception('Invoice draft does not exist');
|
|
}
|
|
// Create an economic object
|
|
$economic = new economic();
|
|
// Get the invoice draft id from the external id
|
|
$invoice_draft_id = $economic->invoices->draft->get_from_external_id($this->external_id->value());
|
|
return (int)$invoice_draft_id;
|
|
}
|
|
|
|
/**
|
|
* Check if the invoice is booked in E-conomic
|
|
* @returns bool If the invoice is booked
|
|
* @throws Exception If the request was not successful
|
|
* @throws Exception If the invoice collection is not set
|
|
*/
|
|
public function isBooked(): bool
|
|
{
|
|
// Require the invoice collection to be selected
|
|
self::requireSelected();
|
|
// Get the invoice booked id from the external id
|
|
try {
|
|
self::getInvoiceBookedId();
|
|
return true;
|
|
} catch (Exception $e) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the invoice booked id from the external id
|
|
* @return int The invoice booked id
|
|
* @throws Exception If the request was not successful
|
|
* @throws Exception If the invoice booked was not found
|
|
*/
|
|
public function getInvoiceBookedId(): int
|
|
{
|
|
// Require the invoice collection to be selected
|
|
self::requireSelected();
|
|
// Check if the invoice_booked_id is already set (We don't want to make a request to E-conomic if we already have the id - Since this is slow.)
|
|
if (!empty($this->booked_invoice_id->value())) {
|
|
return (int)$this->booked_invoice_id->value();
|
|
}
|
|
// If the external id is empty, the invoice booked does not exist
|
|
if ($this->external_id->value() === null) {
|
|
throw new Exception('Invoice booked does not exist');
|
|
}
|
|
// Create an economic object
|
|
$economic = new economic();
|
|
// Get the invoice booked id from the external id
|
|
$invoice_booked_id = $economic->invoices->booked->get_from_external_id($this->external_id->value());
|
|
$this->booked_invoice_id->set($invoice_booked_id);
|
|
return (int)$invoice_booked_id;
|
|
}
|
|
|
|
/**
|
|
* Check if a customer has an open invoice collection
|
|
* @param int $customer_number The E-conomic customer number
|
|
* @return bool If the customer has an open invoice collection
|
|
*/
|
|
public function hasOpenInvoiceCollection(int $customer_number): bool
|
|
{
|
|
$collections = self::getFieldsWhere(
|
|
[
|
|
'customer_number' => $customer_number,
|
|
'closed_at' => null,
|
|
],
|
|
['id']
|
|
);
|
|
return !empty($collections);
|
|
}
|
|
|
|
/**
|
|
* Get the open invoice collections for a customer
|
|
* @param int $customer_number The E-conomic customer number
|
|
* @return array The open invoice collections
|
|
* @throws Exception If the request was not successful
|
|
*/
|
|
public function getOpenInvoiceCollections(int $customer_number): array
|
|
{
|
|
$collections = self::getFieldsWhere(
|
|
[
|
|
'customer_number' => $customer_number,
|
|
'closed_at' => null,
|
|
],
|
|
['id', 'name', 'notes', 'processor', 'external_id', 'created_at', 'updated_at', 'closed_at']
|
|
);
|
|
// Parse the results
|
|
$result = [];
|
|
foreach ( $collections as $collection ) {
|
|
$this->id = $collection['id'];
|
|
self::getObjectProperties();
|
|
self::requireSelected();
|
|
$result[] = self::asArray();
|
|
}
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @param int $customer_number The E-conomic customer number
|
|
* @return self The latest open invoice collection
|
|
* @throws Exception If no open invoice collections were found
|
|
* @throws Exception If the request was not successful
|
|
*/
|
|
public function getLatestOpenInvoiceCollection(int $customer_number): self
|
|
{
|
|
$collections = self::getFieldsWhere(
|
|
[
|
|
'customer_number' => $customer_number,
|
|
'closed_at' => null,
|
|
],
|
|
['id', 'name', 'notes', 'processor', 'external_id', 'created_at', 'updated_at', 'closed_at'],
|
|
);
|
|
if (empty($collections)) {
|
|
throw new Exception('No open invoice collections found');
|
|
}
|
|
// Parse the results
|
|
$this->id = $collections[0]['id'];
|
|
self::getObjectProperties();
|
|
self::requireSelected();
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Add the invoice collection to E-conomic
|
|
* @param bool $ignore_closed If the function should ignore when the invoice collection is closed
|
|
* @throws Exception If the request was not successful
|
|
* @throws Exception If the invoice collection was not found
|
|
* @throws Exception If the customer number is not set
|
|
* @throws Exception If the invoice collection is already closed
|
|
*/
|
|
public function addToEconomic(bool $ignore_closed = false): self
|
|
{
|
|
// Require the invoice collection to be selected
|
|
self::requireSelected();
|
|
// Require the customer number to be set
|
|
if (empty($this->customer_number->value())) {
|
|
throw new Exception('Customer number is not set');
|
|
}
|
|
if (!$ignore_closed) {
|
|
// Require the invoice collection to be open
|
|
self::requireOpen();
|
|
}
|
|
|
|
// Create an economic draft
|
|
self::createInvoiceDraft();
|
|
|
|
// Add the invoices to the invoice draft
|
|
self::addInvoicesToDraft();
|
|
|
|
// Close the invoice collection
|
|
// If the invoice collection is closed, we don't want to close it again
|
|
if ($this->closed_at->value() === null) {
|
|
self::closeCollection();
|
|
}
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Require the invoice collection to be open
|
|
* @throws Exception If the invoice collection is closed
|
|
* @throws Exception If the request was not successful
|
|
*/
|
|
public function requireOpen(): void
|
|
{
|
|
// Require the invoice collection to be selected
|
|
self::requireSelected();
|
|
// Require the invoice collection to not be closed
|
|
if (!empty($this->closed_at->value())) {
|
|
throw new Exception('Invoice collection is closed');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create an invoice draft in E-conomic
|
|
* @throws Exception If the request was not successful
|
|
* @throws Exception If the invoice collection is not set
|
|
* @throws Exception If the invoice collection is already closed
|
|
* @throws Exception If the invoice draft already exists
|
|
*/
|
|
public function createInvoiceDraft(): self
|
|
{
|
|
// Require the invoice collection to be selected
|
|
self::requireSelected();
|
|
// Check if the invoice draft already exists
|
|
if (self::isDraftExisting()) {
|
|
throw new Exception('Invoice draft already exists');
|
|
}
|
|
// Require the invoice draft to not already exist
|
|
self::requireInvoiceDraftDoesNotExist();
|
|
// Create the invoice draft
|
|
$economic = new economic();
|
|
$response = $economic->invoices->drafts->add(
|
|
$this->customer_number->value(),
|
|
self::getExternalId(),
|
|
);
|
|
// Validate the response, by checking if the external id is set
|
|
if (empty($response->references->other)) {
|
|
throw new Exception('Invoice collection was not created successfully');
|
|
}
|
|
// Set the processor
|
|
$this->processor->set(1);
|
|
// Object changed
|
|
self::objectChanged();
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Require the invoice draft to not already exist
|
|
* @throws Exception If the request was not successful
|
|
* @throws Exception If the invoice draft already exists
|
|
*/
|
|
public function requireInvoiceDraftDoesNotExist(): void
|
|
{
|
|
// Require the invoice collection to be selected
|
|
self::requireSelected();
|
|
// Try to get the invoice draft id from the external id
|
|
try {
|
|
self::getInvoiceDraftId();
|
|
throw new Exception('Invoice draft already exists');
|
|
} catch (Exception $e) {
|
|
// Ignore the exception, as it is expected.
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a new collected order invoice
|
|
* @param int $customer_number The E-conomic customer number
|
|
* @param string|null $name The name of the invoice (optional)
|
|
* @param string|null $notes The notes for the invoice (optional)
|
|
* @param int|null $processor The processor id (optional)
|
|
* @return self The created object
|
|
* @throws Exception If the object was not created successfully
|
|
*/
|
|
public function add(int $customer_number, ?string $name = null, ?string $notes = null, ?int $processor = null): self
|
|
{
|
|
global /** @var db $db */
|
|
$db;
|
|
// Sanitize the input
|
|
$customer_number = $db->escape_string($customer_number);
|
|
if (!empty($name)) {
|
|
$name = $db->escape_string($name);
|
|
}
|
|
if (!empty($notes)) {
|
|
$notes = $db->escape_string($notes);
|
|
}
|
|
if (!empty($processor)) {
|
|
$processor = $db->escape_string($processor);
|
|
}
|
|
// Require the customer number to be of a valid customer
|
|
self::requireValidCustomer($customer_number);
|
|
// Add the object
|
|
$tmp_id = self::add_object([
|
|
'customer_number' => (int)$customer_number,
|
|
'name' => $name,
|
|
'notes' => $notes
|
|
]);
|
|
self::select((int)$tmp_id);
|
|
self::requireSelected();
|
|
// Set the processor
|
|
if (!empty($processor)) {
|
|
$this->processor->set($processor);
|
|
}
|
|
self::objectChanged();
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Require the E-conomic customer number to be a valid customer
|
|
* @throws Exception If the customer number is not a valid customer
|
|
*/
|
|
private static function requireValidCustomer(string $customer_number): void
|
|
{
|
|
$customers = new users_o();
|
|
$customers->select($customer_number);
|
|
$customers->requireSelected();
|
|
}
|
|
|
|
public function objectChanged(): void
|
|
{
|
|
//TODO: Add cache invalidation
|
|
}
|
|
|
|
/**
|
|
* Get the external id of the invoice collection
|
|
* @throws Exception If the request was not successful
|
|
* @throws Exception If the UUID could not be generated
|
|
* @throws Exception If the external id is not unique
|
|
*/
|
|
public function getExternalId(): string
|
|
{
|
|
if (empty($this->external_id->value())) {
|
|
$this->external_id->set(self::generateExternalId());
|
|
}
|
|
return $this->external_id->value();
|
|
}
|
|
|
|
/**
|
|
* Generate a unique external id (UUID)
|
|
* @return string The generated UUID
|
|
* @throws Exception If the UUID could not be generated
|
|
* @throws Exception If the request was not successful
|
|
*/
|
|
public static function generateExternalId(): string
|
|
{
|
|
$uuid = bin2hex(random_bytes(16));
|
|
return substr($uuid, 0, 8) . '-' . substr($uuid, 8, 4) . '-' . substr($uuid, 12, 4) . '-' . substr($uuid, 16, 4) . '-' . substr($uuid, 20);
|
|
}
|
|
|
|
/**
|
|
* Add the invoices to the invoice draft
|
|
* @throws Exception If the request was not successful
|
|
* @throws Exception If the invoice collection is not set
|
|
* @throws Exception If the invoice collection is already closed
|
|
* @throws Exception If the invoice draft was not found
|
|
*/
|
|
public function addInvoicesToDraft(): self
|
|
{
|
|
// Require the invoice collection to be selected
|
|
self::requireSelected();
|
|
// Require the invoice collection to be open
|
|
self::requireInvoiceIsNotBooked();
|
|
// Get the orders in the invoice collection
|
|
$orders = self::getOrders();
|
|
// Check if there are any orders in the invoice collection
|
|
if (empty($orders)) {
|
|
throw new Exception('No orders in invoice collection');
|
|
}
|
|
// Require the invoice draft to be set (and exists)
|
|
self::requireInvoiceDraft();
|
|
|
|
// Add the invoices to the invoice draft
|
|
foreach ( $orders as $order ) {
|
|
self::addInvoiceToDraft($order['id']);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Require the invoice collection to not be booked
|
|
* @throws Exception If the request was not successful
|
|
* @throws Exception If the invoice collection is already closed
|
|
*/
|
|
public function requireInvoiceIsNotBooked(): void
|
|
{
|
|
// Require the invoice collection to be selected
|
|
self::requireSelected();
|
|
// Require the invoice collection to not be booked
|
|
if (!empty($this->booked_invoice_id->value())) {
|
|
throw new Exception('Invoice collection is already booked');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Require the invoice draft to be set
|
|
* @throws Exception If the request was not successful
|
|
* @throws Exception If the invoice draft was not found
|
|
*/
|
|
public function requireInvoiceDraft(): void
|
|
{
|
|
// Require the invoice collection to be selected
|
|
self::requireSelected();
|
|
// Require the invoice draft to be set
|
|
if (empty($this->external_id->value())) {
|
|
throw new Exception('Invoice draft is not set');
|
|
}
|
|
|
|
// Require the invoice draft to exist
|
|
self::requireInvoiceDraftExists();
|
|
}
|
|
|
|
/**
|
|
* Require the invoice draft to exist (in E-conomic)
|
|
* @throws Exception If the request was not successful
|
|
* @throws Exception If the invoice draft was not found
|
|
*/
|
|
public function requireInvoiceDraftExists(): void
|
|
{
|
|
// Require the invoice collection to be selected
|
|
self::requireSelected();
|
|
// Get the invoice draft id from the external id
|
|
$invoice_draft_id = self::getInvoiceDraftId();
|
|
// Check if the invoice draft id is set
|
|
if (empty($invoice_draft_id)) {
|
|
throw new Exception('Invoice draft not found');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add an invoice to the invoice draft
|
|
* @param int $order_id The order id to add
|
|
* @throws Exception If the request was not successful
|
|
* @throws Exception If the invoice collection is not set
|
|
* @throws Exception If the invoice collection is already closed
|
|
* @throws Exception If the invoice draft was not found
|
|
*/
|
|
public function addInvoiceToDraft(int $order_id): self
|
|
{
|
|
// Require the invoice collection to be selected
|
|
self::requireSelected();
|
|
// Require the invoice collection to be open
|
|
self::requireInvoiceIsNotBooked();
|
|
// Get the order object
|
|
$order = new orders_o();
|
|
$order->select($order_id);
|
|
$order->requireSelected();
|
|
// Require the invoice draft to be set (and exists)
|
|
self::requireInvoiceDraft();
|
|
|
|
// Add the invoice to the invoice draft
|
|
$economic = new economic();
|
|
$economic->invoices->draft->add_order(
|
|
self::getInvoiceDraftId(),
|
|
$order
|
|
);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Close the invoice collection
|
|
* @throws Exception If the request was not successful
|
|
* @throws Exception If the invoice collection is not set
|
|
* @throws Exception If the invoice collection is already closed
|
|
*/
|
|
public function closeCollection(): self
|
|
{
|
|
// Require the invoice collection to be selected
|
|
self::requireSelected();
|
|
// Require the invoice collection to be open
|
|
self::requireOpen();
|
|
// Close the invoice collection
|
|
$this->closed_at->set(date('Y-m-d H:i:s'));
|
|
self::objectChanged();
|
|
return $this;
|
|
}
|
|
|
|
|
|
} |