Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
27.08% covered (danger)
27.08%
13 / 48
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
ReleaseInvoiceService
27.08% covered (danger)
27.08%
13 / 48
0.00% covered (danger)
0.00%
0 / 4
67.83
0.00% covered (danger)
0.00%
0 / 1
 release
61.90% covered (warning)
61.90%
13 / 21
0.00% covered (danger)
0.00%
0 / 1
7.99
 verifyInvoiceItemHasLinkPurchaseItem
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
20
 processActivetyLog
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 validateHasPOInvoiceItemAutomatic
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace Domain\Invoice\Services;
4
5use App\Events\ServiceProcessed;
6use App\Models\Invoice\Invoice;
7use App\Models\PurchaseOrder\PurchaseOrderInvoiceItem;
8use App\Models\User;
9use Domain\Invoice\DTOs\StoreInvoiceActivityLogDTO;
10use Domain\Invoice\Enums\InvoiceActivetyLogOperationEnum;
11use Domain\Invoice\Enums\InvoiceItemStatusEnum;
12use Domain\Invoice\Enums\InvoicePurchaseOrderStatusEnum;
13use Domain\Invoice\Enums\InvoiceStatusEnum;
14use Domain\Invoice\Exceptions\InvoiceItemNoHasLinkPurchaseItemException;
15use Domain\Invoice\Exceptions\InvoiceReleasedException;
16use Domain\PurchaseOrder\Enums\PurchaseOrderInvoiceItemStatusEnum;
17use Illuminate\Support\Facades\DB;
18use Throwable;
19
20readonly class ReleaseInvoiceService
21{
22    /**
23     * @throws Throwable
24     */
25    public function release(Invoice $invoice, User $user): void
26    {
27        try {
28            DB::beginTransaction();
29
30            if ($invoice->isReleased()) {
31                throw new InvoiceReleasedException(__('Invoice has already been released'));
32            }
33
34            $invoiceOldJson = json_encode($invoice->getAttributes());
35
36            $invoicePurchaseOrderStatus = $invoice->getAttribute('purchase_order_status');
37
38            if ($invoicePurchaseOrderStatus != InvoicePurchaseOrderStatusEnum::VALIDATED) {
39                if ($invoicePurchaseOrderStatus == InvoicePurchaseOrderStatusEnum::AUTOMATIC &&
40                   $this->validateHasPOInvoiceItemAutomatic($invoice)) {
41                    $invoice->setAttribute('purchase_order_status', InvoicePurchaseOrderStatusEnum::VALIDATED);
42                } else {
43                    throw new InvoiceReleasedException(
44                        __('Is not possible to release the invoice when purchase order status is different of validated')
45                    );
46                }
47            }
48
49            $this->verifyInvoiceItemHasLinkPurchaseItem($invoice);
50
51            $invoice->setAttribute('status', InvoiceStatusEnum::RELEASED);
52            $invoice->save();
53
54            $this->processActivetyLog($invoice, $user, $invoiceOldJson);
55
56            DB::commit();
57        } catch (Throwable $exception) {
58            DB::rollBack();
59            throw $exception;
60        }
61
62        ServiceProcessed::dispatch(self::class, $user);
63    }
64
65    /**
66     * @throws InvoiceItemNoHasLinkPurchaseItemException
67     */
68    private function verifyInvoiceItemHasLinkPurchaseItem(Invoice $invoice)
69    {
70        foreach ($invoice->invoiceItem as $invoiceItem) {
71            $purchaseOrderInvoiceItem = PurchaseOrderInvoiceItem::query()
72                ->where('invoice_item_id', $invoiceItem->getKey())
73                ->where('status', PurchaseOrderInvoiceItemStatusEnum::VALIDATED)
74                ->first();
75
76            if (
77                is_null($purchaseOrderInvoiceItem) ||
78                $invoiceItem->getAttribute('status') !== InvoiceItemStatusEnum::VALIDATED
79            ) {
80                throw new InvoiceItemNoHasLinkPurchaseItemException(
81                    __('The invoice item is not linked to a purchase order item. Invoice Item: :invoiceItem',
82                        ['invoiceItem' => $invoiceItem->getKey(),
83                        ])
84                );
85            }
86        }
87    }
88
89    private function processActivetyLog(Invoice $invoice, User $user, $details)
90    {
91        $invoiceActivityLogDTO = new StoreInvoiceActivityLogDTO([
92            'entity_type' => Invoice::class,
93            'entity_id' => $invoice->getKey(),
94            'operation' => InvoiceActivetyLogOperationEnum::UPDATE,
95            'user' => $user,
96            'detail' => $details,
97            'observation' => 'Nota fiscal liberada.',
98        ]);
99
100        (new InvoiceActivityLogStoreService)->store($invoiceActivityLogDTO, $invoice, $user);
101    }
102
103    private function validateHasPOInvoiceItemAutomatic(Invoice $invoice): bool
104    {
105        $purchaseOrderInvoiceItemExists = PurchaseOrderInvoiceItem::query()
106            ->join('invoice_items', 'invoice_items.id', '=', 'purchase_order_invoice_items.invoice_item_id')
107            ->where('invoice_items.invoice_id', $invoice->getKey())
108            ->where('purchase_order_invoice_items.status', PurchaseOrderInvoiceItemStatusEnum::AUTOMATIC)
109            ->exists();
110
111        return ! $purchaseOrderInvoiceItemExists;
112    }
113}