Add booked invoice helper and retrieval methods
- Introduce `economic_invoice_booked` helper for normalizing booked invoice data. - Add `getFromId` method to fetch booked invoice details by ID. - Extend `economic` class with `getInvoiceBookedFromExternalId` for external ID-based retrieval. - Update imports and economic helper class registrations to include booked invoice functionality.
This commit is contained in:
@@ -23,6 +23,7 @@ use endpoints\economic_payment_terms_endpoint;
|
||||
use endpoints\economic_products_endpoint;
|
||||
use helpers\economic_customer;
|
||||
use helpers\economic_invoice_draft;
|
||||
use helpers\economic_invoice_booked;
|
||||
use helpers\economic_tasks;
|
||||
use interfaces\economic_i;
|
||||
|
||||
@@ -141,4 +142,15 @@ class economic implements economic_i
|
||||
|
||||
return new $this->helpers->economic_customer($customer_number);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getInvoiceBookedFromExternalId(string $external_id): economic_invoice_booked
|
||||
{
|
||||
$bookedId = $this->invoices->booked->get_from_external_id($external_id);
|
||||
$raw = $this->invoices->booked->getFromId($bookedId);
|
||||
// Return the booked invoice helper
|
||||
return (new economic_invoice_booked($raw));
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
<?php
|
||||
require_once WD . '/modules/economic/helpers/economic_customer.php';
|
||||
require_once WD . '/modules/economic/helpers/economic_invoice_draft.php';
|
||||
require_once WD . '/modules/economic/helpers/economic_invoice_booked.php';
|
||||
require_once WD . '/modules/economic/helpers/economic_tasks.php';
|
||||
|
||||
|
||||
use helpers\economic_customer;
|
||||
use helpers\economic_invoice_draft;
|
||||
use helpers\economic_invoice_booked;
|
||||
use helpers\economic_tasks;
|
||||
|
||||
class economic_helpers
|
||||
@@ -20,6 +22,11 @@ class economic_helpers
|
||||
* @var string $economic_invoice_draft
|
||||
*/
|
||||
public string $economic_invoice_draft = economic_invoice_draft::class;
|
||||
/**
|
||||
* The Economic booked invoice helper
|
||||
* @var string $economic_invoice_booked
|
||||
*/
|
||||
public string $economic_invoice_booked = economic_invoice_booked::class;
|
||||
/**
|
||||
* The Economic tasks helper
|
||||
* @var string $economic_tasks
|
||||
|
||||
+10
@@ -66,4 +66,14 @@ class economic_invoices_booked_endpoint
|
||||
}
|
||||
}
|
||||
|
||||
public function getFromId(int $invoice_id): object
|
||||
{
|
||||
$response = $this->send_request(
|
||||
'/invoices/booked/' . $invoice_id,
|
||||
'GET'
|
||||
);
|
||||
// Return the response as an object
|
||||
return json_decode($response);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,346 @@
|
||||
<?php
|
||||
|
||||
namespace helpers;
|
||||
|
||||
/**
|
||||
* Helper to normalize/booked invoice data from E-conomic (stdClass) to a PHP array structure
|
||||
* with snake_case keys and convenient totals.
|
||||
*
|
||||
* This helper also exposes documented object properties mirroring the most relevant
|
||||
* top-level fields from the E‑conomic booked invoice payload for easier IDE discovery.
|
||||
*
|
||||
* Notes on mapping (E‑conomic → helper):
|
||||
* - bookedInvoiceNumber → booked_invoice_number
|
||||
* - orderNumber → order_number
|
||||
* - date → date (Y-m-d)
|
||||
* - currency → currency (e.g. DKK)
|
||||
* - exchangeRate → exchange_rate
|
||||
* - netAmount → net_amount
|
||||
* - netAmountInBaseCurrency → net_amount_in_base_currency
|
||||
* - grossAmount → gross_amount (also exposed as total_amount)
|
||||
* - grossAmountInBaseCurrency → gross_amount_in_base_currency
|
||||
* - vatAmount → vat_amount
|
||||
* - roundingAmount → rounding_amount
|
||||
* - remainder → remainder
|
||||
* - remainderInBaseCurrency → remainder_in_base_currency
|
||||
* - dueDate → due_date (Y-m-d)
|
||||
* - paymentTerms → payment_terms (array)
|
||||
* - customer → customer (array)
|
||||
* - recipient → recipient (array with nested vat_zone)
|
||||
* - references → references (array, typically contains other)
|
||||
* - layout → layout (array)
|
||||
* - pdf → pdf (array with download)
|
||||
* - lines → lines (array of line arrays)
|
||||
* - sent → sent (string URL) if present
|
||||
* - self → self (string URL) if present
|
||||
*
|
||||
* Convenience fields:
|
||||
* - total_amount (float) mirrors gross_amount
|
||||
* - total_net_amount (float) mirrors net_amount
|
||||
*/
|
||||
class economic_invoice_booked
|
||||
{
|
||||
/**
|
||||
* Raw booked invoice data as returned by the E-conomic API (stdClass)
|
||||
* @var object
|
||||
*/
|
||||
protected object $booked_invoice_data;
|
||||
|
||||
/**
|
||||
* Normalized array (snake_case keys)
|
||||
* @var array
|
||||
*/
|
||||
protected array $normalized = [];
|
||||
|
||||
/**
|
||||
* The booked invoice number in E‑conomic.
|
||||
* Origin: bookedInvoiceNumber
|
||||
* @var int
|
||||
*/
|
||||
public int $booked_invoice_number = 0;
|
||||
|
||||
/**
|
||||
* The order number associated with the invoice, if any.
|
||||
* Origin: orderNumber
|
||||
* @var int|null
|
||||
*/
|
||||
public ?int $order_number = null;
|
||||
|
||||
/**
|
||||
* Invoice booking date (YYYY-MM-DD).
|
||||
* Origin: date
|
||||
* @var string
|
||||
*/
|
||||
public string $date = '';
|
||||
|
||||
/**
|
||||
* Invoice currency code (e.g., DKK).
|
||||
* Origin: currency
|
||||
* @var string
|
||||
*/
|
||||
public string $currency = '';
|
||||
|
||||
/**
|
||||
* Exchange rate for the invoice currency relative to base currency.
|
||||
* Origin: exchangeRate
|
||||
* @var float
|
||||
*/
|
||||
public float $exchange_rate = 1.0;
|
||||
|
||||
/**
|
||||
* Net amount in invoice currency.
|
||||
* Origin: netAmount
|
||||
* @var float
|
||||
*/
|
||||
public float $net_amount = 0.0;
|
||||
|
||||
/**
|
||||
* Net amount in base currency.
|
||||
* Origin: netAmountInBaseCurrency
|
||||
* @var float
|
||||
*/
|
||||
public float $net_amount_in_base_currency = 0.0;
|
||||
|
||||
/**
|
||||
* Gross amount in invoice currency (includes VAT if applicable).
|
||||
* Origin: grossAmount
|
||||
* Also exposed as total_amount.
|
||||
* @var float
|
||||
*/
|
||||
public float $gross_amount = 0.0;
|
||||
|
||||
/**
|
||||
* Gross amount in base currency.
|
||||
* Origin: grossAmountInBaseCurrency
|
||||
* @var float
|
||||
*/
|
||||
public float $gross_amount_in_base_currency = 0.0;
|
||||
|
||||
/**
|
||||
* Total VAT amount for the invoice.
|
||||
* Origin: vatAmount
|
||||
* @var float
|
||||
*/
|
||||
public float $vat_amount = 0.0;
|
||||
|
||||
/**
|
||||
* Rounding adjustment amount.
|
||||
* Origin: roundingAmount
|
||||
* @var float
|
||||
*/
|
||||
public float $rounding_amount = 0.0;
|
||||
|
||||
/**
|
||||
* Remainder amount (outstanding) in invoice currency.
|
||||
* Origin: remainder
|
||||
* @var float
|
||||
*/
|
||||
public float $remainder = 0.0;
|
||||
|
||||
/**
|
||||
* Remainder amount (outstanding) in base currency.
|
||||
* Origin: remainderInBaseCurrency
|
||||
* @var float
|
||||
*/
|
||||
public float $remainder_in_base_currency = 0.0;
|
||||
|
||||
/**
|
||||
* Invoice due date (YYYY-MM-DD) when applicable.
|
||||
* Origin: dueDate
|
||||
* @var string|null
|
||||
*/
|
||||
public ?string $due_date = null;
|
||||
|
||||
/**
|
||||
* Payment terms info.
|
||||
* Origin: paymentTerms
|
||||
* @var array{payment_terms_number?:int,days_of_credit?:int,name?:string,payment_terms_type?:string,self?:string}
|
||||
*/
|
||||
public array $payment_terms = [];
|
||||
|
||||
/**
|
||||
* Customer reference.
|
||||
* Origin: customer
|
||||
* @var array{customer_number?:int,self?:string}
|
||||
*/
|
||||
public array $customer = [];
|
||||
|
||||
/**
|
||||
* Recipient details and VAT zone.
|
||||
* Origin: recipient
|
||||
* @var array{name?:string,address?:string,zip?:string|int,city?:string,vat_zone?:array{name?:string,vat_zone_number?:int,enabled_for_customer?:int|bool,enabled_for_supplier?:int|bool,self?:string}}
|
||||
*/
|
||||
public array $recipient = [];
|
||||
|
||||
/**
|
||||
* References (e.g., other/external id).
|
||||
* Origin: references
|
||||
* @var array{other?:string}
|
||||
*/
|
||||
public array $references = [];
|
||||
|
||||
/**
|
||||
* Layout reference used for the invoice.
|
||||
* Origin: layout
|
||||
* @var array{layout_number?:int,self?:string}
|
||||
*/
|
||||
public array $layout = [];
|
||||
|
||||
/**
|
||||
* PDF info (download link).
|
||||
* Origin: pdf
|
||||
* @var array{download?:string}
|
||||
*/
|
||||
public array $pdf = [];
|
||||
|
||||
/**
|
||||
* Invoice lines (normalized to snake_case keys).
|
||||
* Origin: lines
|
||||
* @var array<int, array{
|
||||
* line_number?:int,sort_key?:int,description?:string,
|
||||
* quantity?:int|float,unit_net_price?:int|float,discount_percentage?:int|float,
|
||||
* unit_cost_price?:int|float,vat_rate?:int|float,vat_amount?:int|float,total_net_amount?:int|float,
|
||||
* product?:array{product_number?:int|string,self?:string},
|
||||
* departmental_distribution?:array{
|
||||
* departmental_distribution_number?:int,name?:string,barred?:bool|int|null,distribution_type?:string,
|
||||
* distributions?:array<int, array{percentage?:int|float,department?:array{department_number?:int,self?:string}}>,
|
||||
* self?:string
|
||||
* }
|
||||
* }>
|
||||
*/
|
||||
public array $lines = [];
|
||||
|
||||
/**
|
||||
* Link to E‑conomic API endpoint indicating sent status (if present).
|
||||
* Origin: sent
|
||||
* @var string|null
|
||||
*/
|
||||
public ?string $sent = null;
|
||||
|
||||
/**
|
||||
* Self link to this resource in E‑conomic.
|
||||
* Origin: self
|
||||
* @var string|null
|
||||
*/
|
||||
public ?string $self = null;
|
||||
|
||||
/**
|
||||
* Convenience: mirrors gross_amount.
|
||||
* @var float
|
||||
*/
|
||||
public float $total_amount = 0.0;
|
||||
|
||||
/**
|
||||
* Convenience: mirrors net_amount.
|
||||
* @var float
|
||||
*/
|
||||
public float $total_net_amount = 0.0;
|
||||
|
||||
public function __construct(object $booked_invoice_data)
|
||||
{
|
||||
$this->booked_invoice_data = $booked_invoice_data;
|
||||
// Prepare normalized structure immediately
|
||||
$this->normalized = $this->normalize($this->booked_invoice_data);
|
||||
// Provide convenience totals expected by internal callers
|
||||
if (!isset($this->normalized['total_amount']) && isset($this->normalized['gross_amount'])) {
|
||||
$this->normalized['total_amount'] = (float)$this->normalized['gross_amount'];
|
||||
}
|
||||
if (!isset($this->normalized['total_net_amount']) && isset($this->normalized['net_amount'])) {
|
||||
$this->normalized['total_net_amount'] = (float)$this->normalized['net_amount'];
|
||||
}
|
||||
// Populate documented properties for IDE discoverability
|
||||
$this->populatePropertiesFromArray($this->normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the booked invoice stdClass into a normalized array (snake_case keys)
|
||||
* and add convenience fields like total_amount and total_net_amount.
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return $this->normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively normalize stdClass/array values to arrays with snake_case keys
|
||||
* while preserving scalars.
|
||||
* @param mixed $value
|
||||
* @return mixed
|
||||
*/
|
||||
protected function normalize($value)
|
||||
{
|
||||
if (is_object($value)) {
|
||||
// Convert stdClass to assoc array with snake_case keys
|
||||
$result = [];
|
||||
foreach (get_object_vars($value) as $k => $v) {
|
||||
$key = $this->camelToSnake((string)$k);
|
||||
$result[$key] = $this->normalize($v);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
if (is_array($value)) {
|
||||
$result = [];
|
||||
foreach ($value as $k => $v) {
|
||||
// Preserve numeric array indexes; convert associative keys to snake_case
|
||||
if (is_string($k)) {
|
||||
$key = $this->camelToSnake($k);
|
||||
$result[$key] = $this->normalize($v);
|
||||
} else {
|
||||
$result[$k] = $this->normalize($v);
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
// Scalars: cast numeric strings to appropriate numeric types when reasonable
|
||||
if (is_string($value)) {
|
||||
if (is_numeric($value)) {
|
||||
// Decide int vs float based on presence of decimal point
|
||||
return (strpos($value, '.') !== false) ? (float)$value : (int)$value;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function camelToSnake(string $input): string
|
||||
{
|
||||
// Preserve common abbreviations and already snake keys
|
||||
if (strpos($input, '_') !== false) {
|
||||
return strtolower($input);
|
||||
}
|
||||
$snake = preg_replace('/(?<!^)[A-Z]/', '_$0', $input);
|
||||
return strtolower($snake);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate documented public properties from the normalized array.
|
||||
*/
|
||||
private function populatePropertiesFromArray(array $data): void
|
||||
{
|
||||
$this->booked_invoice_number = (int)($data['booked_invoice_number'] ?? 0);
|
||||
$this->order_number = isset($data['order_number']) ? (int)$data['order_number'] : null;
|
||||
$this->date = (string)($data['date'] ?? '');
|
||||
$this->currency = (string)($data['currency'] ?? '');
|
||||
$this->exchange_rate = (float)($data['exchange_rate'] ?? 1.0);
|
||||
$this->net_amount = (float)($data['net_amount'] ?? 0.0);
|
||||
$this->net_amount_in_base_currency = (float)($data['net_amount_in_base_currency'] ?? 0.0);
|
||||
$this->gross_amount = (float)($data['gross_amount'] ?? 0.0);
|
||||
$this->gross_amount_in_base_currency = (float)($data['gross_amount_in_base_currency'] ?? 0.0);
|
||||
$this->vat_amount = (float)($data['vat_amount'] ?? 0.0);
|
||||
$this->rounding_amount = (float)($data['rounding_amount'] ?? 0.0);
|
||||
$this->remainder = (float)($data['remainder'] ?? 0.0);
|
||||
$this->remainder_in_base_currency = (float)($data['remainder_in_base_currency'] ?? 0.0);
|
||||
$this->due_date = isset($data['due_date']) && $data['due_date'] !== '' ? (string)$data['due_date'] : null;
|
||||
$this->payment_terms = isset($data['payment_terms']) && is_array($data['payment_terms']) ? $data['payment_terms'] : [];
|
||||
$this->customer = isset($data['customer']) && is_array($data['customer']) ? $data['customer'] : [];
|
||||
$this->recipient = isset($data['recipient']) && is_array($data['recipient']) ? $data['recipient'] : [];
|
||||
$this->references = isset($data['references']) && is_array($data['references']) ? $data['references'] : [];
|
||||
$this->layout = isset($data['layout']) && is_array($data['layout']) ? $data['layout'] : [];
|
||||
$this->pdf = isset($data['pdf']) && is_array($data['pdf']) ? $data['pdf'] : [];
|
||||
$this->lines = isset($data['lines']) && is_array($data['lines']) ? $data['lines'] : [];
|
||||
$this->sent = isset($data['sent']) ? (string)$data['sent'] : null;
|
||||
$this->self = isset($data['self']) ? (string)$data['self'] : null;
|
||||
$this->total_amount = (float)($data['total_amount'] ?? $this->gross_amount);
|
||||
$this->total_net_amount = (float)($data['total_net_amount'] ?? $this->net_amount);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user