update
This commit is contained in:
@@ -3,11 +3,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Modules\Automation;
|
||||
|
||||
use App\Core\Http\ToggleableRepositoryTrait;
|
||||
use PDO;
|
||||
use Throwable;
|
||||
|
||||
final class AutomationRepository
|
||||
{
|
||||
use ToggleableRepositoryTrait {
|
||||
toggleActive as private traitToggleActive;
|
||||
}
|
||||
public function __construct(
|
||||
private readonly PDO $pdo
|
||||
) {
|
||||
@@ -150,10 +154,7 @@ final class AutomationRepository
|
||||
|
||||
public function toggleActive(int $id): void
|
||||
{
|
||||
$statement = $this->pdo->prepare(
|
||||
'UPDATE automation_rules SET is_active = NOT is_active WHERE id = :id'
|
||||
);
|
||||
$statement->execute(['id' => $id]);
|
||||
$this->traitToggleActive('automation_rules', $id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Modules\Automation;
|
||||
|
||||
use App\Modules\Accounting\ReceiptIssueException;
|
||||
use App\Modules\Accounting\ReceiptRepository;
|
||||
use App\Modules\Accounting\ReceiptService;
|
||||
use App\Modules\Email\EmailSendingService;
|
||||
use App\Modules\Orders\OrdersRepository;
|
||||
use App\Modules\Settings\CompanySettingsRepository;
|
||||
@@ -36,7 +38,8 @@ final class AutomationService
|
||||
private readonly CompanySettingsRepository $companySettings,
|
||||
private readonly ReceiptRepository $receipts,
|
||||
private readonly ReceiptConfigRepository $receiptConfigs,
|
||||
private readonly ShipmentPackageRepository $shipmentPackages
|
||||
private readonly ShipmentPackageRepository $shipmentPackages,
|
||||
private readonly ReceiptService $receiptService
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -347,22 +350,8 @@ final class AutomationService
|
||||
if ($configId <= 0) {
|
||||
return;
|
||||
}
|
||||
$issueDateMode = (string) ($config['issue_date_mode'] ?? 'today');
|
||||
$duplicatePolicy = (string) ($config['duplicate_policy'] ?? 'skip_if_exists');
|
||||
|
||||
$receiptConfig = $this->receiptConfigs->findById($configId);
|
||||
if ($receiptConfig === null || (int) ($receiptConfig['is_active'] ?? 0) !== 1) {
|
||||
$this->orders->recordActivity(
|
||||
$orderId,
|
||||
'automation_receipt_failed',
|
||||
$actorName . ' - nieprawidlowa lub nieaktywna konfiguracja paragonu',
|
||||
['receipt_config_id' => $configId],
|
||||
'system',
|
||||
$actorName
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
$existingReceipts = $this->receipts->findByOrderId($orderId);
|
||||
if ($duplicatePolicy === 'skip_if_exists' && $existingReceipts !== []) {
|
||||
$this->orders->recordActivity(
|
||||
@@ -376,55 +365,23 @@ final class AutomationService
|
||||
return;
|
||||
}
|
||||
|
||||
$details = $this->orders->findDetails($orderId);
|
||||
if ($details === null) {
|
||||
return;
|
||||
}
|
||||
$order = is_array($details['order'] ?? null) ? $details['order'] : [];
|
||||
$items = is_array($details['items'] ?? null) ? $details['items'] : [];
|
||||
$addresses = is_array($details['addresses'] ?? null) ? $details['addresses'] : [];
|
||||
$payments = is_array($details['payments'] ?? null) ? $details['payments'] : [];
|
||||
|
||||
$issueDate = $this->resolveIssueDate($issueDateMode, $order, $payments);
|
||||
$saleDate = $this->resolveSaleDate($receiptConfig, $order, $payments, $issueDate);
|
||||
$orderReference = $this->resolveOrderReference($receiptConfig, $order);
|
||||
$sellerSnapshot = $this->buildSellerSnapshot();
|
||||
$buyerSnapshot = $this->buildBuyerSnapshot($addresses);
|
||||
['items' => $itemsSnapshot, 'total_gross' => $totalGross] = $this->buildItemsSnapshot($items);
|
||||
|
||||
try {
|
||||
$receiptNumber = $this->receipts->getNextNumber(
|
||||
$configId,
|
||||
(string) ($receiptConfig['number_format'] ?? 'PAR/%N/%M/%Y'),
|
||||
(string) ($receiptConfig['numbering_type'] ?? 'monthly')
|
||||
);
|
||||
$this->receipts->create([
|
||||
$result = $this->receiptService->issue([
|
||||
'order_id' => $orderId,
|
||||
'config_id' => $configId,
|
||||
'receipt_number' => $receiptNumber,
|
||||
'issue_date' => $issueDate,
|
||||
'sale_date' => $saleDate,
|
||||
'seller_data_json' => json_encode($sellerSnapshot, JSON_UNESCAPED_UNICODE),
|
||||
'buyer_data_json' => $buyerSnapshot !== null ? json_encode($buyerSnapshot, JSON_UNESCAPED_UNICODE) : null,
|
||||
'items_json' => json_encode($itemsSnapshot, JSON_UNESCAPED_UNICODE),
|
||||
'total_net' => number_format($totalGross, 2, '.', ''),
|
||||
'total_gross' => number_format($totalGross, 2, '.', ''),
|
||||
'order_reference_value' => $orderReference,
|
||||
'issue_date_mode' => (string) ($config['issue_date_mode'] ?? 'today'),
|
||||
'created_by' => null,
|
||||
]);
|
||||
|
||||
$this->orders->recordActivity(
|
||||
$orderId,
|
||||
'receipt_issued',
|
||||
'Wystawiono paragon: ' . $receiptNumber,
|
||||
['receipt_number' => $receiptNumber, 'config_id' => $configId, 'total_gross' => number_format($totalGross, 2, '.', '')],
|
||||
'Wystawiono paragon: ' . $result['receipt_number'],
|
||||
['receipt_number' => $result['receipt_number'], 'config_id' => $configId, 'total_gross' => $result['total_gross']],
|
||||
'system',
|
||||
$actorName
|
||||
);
|
||||
|
||||
// Chain automation: issuing receipt from one rule should trigger
|
||||
// rules listening on receipt.created. Uses generic chain context
|
||||
// with depth + rule deduplication to prevent loops.
|
||||
$this->emitEvent(
|
||||
'receipt.created',
|
||||
$orderId,
|
||||
@@ -432,10 +389,19 @@ final class AutomationService
|
||||
[
|
||||
'automation_source' => 'issue_receipt',
|
||||
'automation_rule' => $ruleName,
|
||||
'receipt_number' => $receiptNumber,
|
||||
'receipt_number' => $result['receipt_number'],
|
||||
'receipt_config_id' => $configId,
|
||||
]
|
||||
);
|
||||
} catch (ReceiptIssueException $e) {
|
||||
$this->orders->recordActivity(
|
||||
$orderId,
|
||||
'automation_receipt_failed',
|
||||
$actorName . ' - ' . $e->getMessage(),
|
||||
['receipt_config_id' => $configId],
|
||||
'system',
|
||||
$actorName
|
||||
);
|
||||
} catch (Throwable $exception) {
|
||||
$this->orders->recordActivity(
|
||||
$orderId,
|
||||
@@ -593,175 +559,6 @@ final class AutomationService
|
||||
return $this->shipmentPackages->findLatestByOrderId($orderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $order
|
||||
* @param list<array<string, mixed>> $payments
|
||||
*/
|
||||
private function resolveIssueDate(string $mode, array $order, array $payments): string
|
||||
{
|
||||
if ($mode === 'order_date') {
|
||||
$orderedAt = trim((string) ($order['ordered_at'] ?? ''));
|
||||
if ($orderedAt !== '') {
|
||||
$timestamp = strtotime($orderedAt);
|
||||
if ($timestamp !== false) {
|
||||
return date('Y-m-d', $timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($mode === 'payment_date') {
|
||||
$firstPayment = $payments[0] ?? [];
|
||||
$paymentDate = trim((string) ($firstPayment['payment_date'] ?? ''));
|
||||
if ($paymentDate !== '') {
|
||||
$timestamp = strtotime($paymentDate);
|
||||
if ($timestamp !== false) {
|
||||
return date('Y-m-d', $timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return date('Y-m-d');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $receiptConfig
|
||||
* @param array<string, mixed> $order
|
||||
* @param list<array<string, mixed>> $payments
|
||||
*/
|
||||
private function resolveSaleDate(array $receiptConfig, array $order, array $payments, string $issueDate): string
|
||||
{
|
||||
$source = (string) ($receiptConfig['sale_date_source'] ?? 'issue_date');
|
||||
|
||||
if ($source === 'order_date') {
|
||||
$ordered = (string) ($order['ordered_at'] ?? '');
|
||||
if ($ordered !== '') {
|
||||
$ts = strtotime($ordered);
|
||||
return $ts !== false ? date('Y-m-d', $ts) : $issueDate;
|
||||
}
|
||||
}
|
||||
|
||||
if ($source === 'payment_date' && $payments !== []) {
|
||||
$lastPayment = $payments[0] ?? [];
|
||||
$payDate = (string) ($lastPayment['payment_date'] ?? '');
|
||||
if ($payDate !== '') {
|
||||
$ts = strtotime($payDate);
|
||||
return $ts !== false ? date('Y-m-d', $ts) : $issueDate;
|
||||
}
|
||||
}
|
||||
|
||||
return $issueDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $receiptConfig
|
||||
* @param array<string, mixed> $order
|
||||
*/
|
||||
private function resolveOrderReference(array $receiptConfig, array $order): ?string
|
||||
{
|
||||
$ref = (string) ($receiptConfig['order_reference'] ?? 'none');
|
||||
|
||||
if ($ref === 'orderpro') {
|
||||
return (string) ($order['internal_order_number'] ?? '');
|
||||
}
|
||||
|
||||
if ($ref === 'integration') {
|
||||
return (string) ($order['external_order_id'] ?? '');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
private function buildSellerSnapshot(): array
|
||||
{
|
||||
$seller = $this->companySettings->getSettings();
|
||||
|
||||
return [
|
||||
'company_name' => $seller['company_name'] ?? '',
|
||||
'tax_number' => $seller['tax_number'] ?? '',
|
||||
'street' => $seller['street'] ?? '',
|
||||
'city' => $seller['city'] ?? '',
|
||||
'postal_code' => $seller['postal_code'] ?? '',
|
||||
'phone' => $seller['phone'] ?? '',
|
||||
'email' => $seller['email'] ?? '',
|
||||
'bank_account' => $seller['bank_account'] ?? '',
|
||||
'bdo_number' => $seller['bdo_number'] ?? '',
|
||||
'regon' => $seller['regon'] ?? '',
|
||||
'court_register' => $seller['court_register'] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array<string, mixed>> $addresses
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
private function buildBuyerSnapshot(array $addresses): ?array
|
||||
{
|
||||
$buyerAddress = $this->resolveBuyerAddress($addresses);
|
||||
if ($buyerAddress === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
'name' => $buyerAddress['name'] ?? '',
|
||||
'company_name' => $buyerAddress['company_name'] ?? '',
|
||||
'tax_number' => $buyerAddress['company_tax_number'] ?? '',
|
||||
'street' => trim((string) (($buyerAddress['street_name'] ?? '') . ' ' . ($buyerAddress['street_number'] ?? ''))),
|
||||
'city' => $buyerAddress['city'] ?? '',
|
||||
'postal_code' => $buyerAddress['zip_code'] ?? '',
|
||||
'phone' => $buyerAddress['phone'] ?? '',
|
||||
'email' => $buyerAddress['email'] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array<string, mixed>> $addresses
|
||||
* @return array<string, mixed>|null
|
||||
*/
|
||||
private function resolveBuyerAddress(array $addresses): ?array
|
||||
{
|
||||
$byType = [];
|
||||
foreach ($addresses as $address) {
|
||||
$type = (string) ($address['address_type'] ?? '');
|
||||
if ($type !== '' && !isset($byType[$type])) {
|
||||
$byType[$type] = $address;
|
||||
}
|
||||
}
|
||||
|
||||
return $byType['invoice'] ?? $byType['customer'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array<string, mixed>> $items
|
||||
* @return array{items:list<array<string,mixed>>,total_gross:float}
|
||||
*/
|
||||
private function buildItemsSnapshot(array $items): array
|
||||
{
|
||||
$itemsSnapshot = [];
|
||||
$totalGross = 0.0;
|
||||
foreach ($items as $item) {
|
||||
$qty = (float) ($item['quantity'] ?? 0);
|
||||
$price = $item['original_price_with_tax'] !== null ? (float) $item['original_price_with_tax'] : 0.0;
|
||||
$lineTotal = $qty * $price;
|
||||
$totalGross += $lineTotal;
|
||||
$itemsSnapshot[] = [
|
||||
'name' => $item['original_name'] ?? '',
|
||||
'quantity' => $qty,
|
||||
'price' => $price,
|
||||
'total' => $lineTotal,
|
||||
'sku' => $item['sku'] ?? '',
|
||||
'ean' => $item['ean'] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'items' => $itemsSnapshot,
|
||||
'total_gross' => $totalGross,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $context
|
||||
* @return array{chain_id:string,depth:int,executions:list<string>}
|
||||
|
||||
Reference in New Issue
Block a user