|
@@ -1,533 +0,0 @@
|
1
|
|
-<?php
|
2
|
|
-
|
3
|
|
-namespace App\Models\Accounting;
|
4
|
|
-
|
5
|
|
-use App\Casts\MoneyCast;
|
6
|
|
-use App\Casts\RateCast;
|
7
|
|
-use App\Collections\Accounting\DocumentCollection;
|
8
|
|
-use App\Enums\Accounting\AdjustmentComputation;
|
9
|
|
-use App\Enums\Accounting\CreditNoteStatus;
|
10
|
|
-use App\Enums\Accounting\DocumentDiscountMethod;
|
11
|
|
-use App\Enums\Accounting\DocumentType;
|
12
|
|
-use App\Enums\Accounting\JournalEntryType;
|
13
|
|
-use App\Enums\Accounting\TransactionType;
|
14
|
|
-use App\Filament\Company\Resources\Sales\CreditNoteResource;
|
15
|
|
-use App\Models\Common\Client;
|
16
|
|
-use App\Models\Company;
|
17
|
|
-use App\Models\Setting\DocumentDefault;
|
18
|
|
-use App\Utilities\Currency\CurrencyAccessor;
|
19
|
|
-use App\Utilities\Currency\CurrencyConverter;
|
20
|
|
-use Filament\Actions\Action;
|
21
|
|
-use Filament\Actions\MountableAction;
|
22
|
|
-use Filament\Actions\ReplicateAction;
|
23
|
|
-use Illuminate\Database\Eloquent\Attributes\CollectedBy;
|
24
|
|
-use Illuminate\Database\Eloquent\Casts\Attribute;
|
25
|
|
-use Illuminate\Database\Eloquent\Model;
|
26
|
|
-use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
27
|
|
-use Illuminate\Database\Eloquent\Relations\MorphMany;
|
28
|
|
-use Illuminate\Database\Eloquent\Relations\MorphOne;
|
29
|
|
-use Illuminate\Support\Carbon;
|
30
|
|
-use Illuminate\Support\Collection;
|
31
|
|
-
|
32
|
|
-#[CollectedBy(DocumentCollection::class)]
|
33
|
|
-class CreditNote extends Document
|
34
|
|
-{
|
35
|
|
- protected $table = 'credit_notes';
|
36
|
|
-
|
37
|
|
- protected $fillable = [
|
38
|
|
- 'company_id',
|
39
|
|
- 'client_id',
|
40
|
|
- 'logo',
|
41
|
|
- 'header',
|
42
|
|
- 'subheader',
|
43
|
|
- 'credit_note_number',
|
44
|
|
- 'reference_number',
|
45
|
|
- 'date',
|
46
|
|
- 'approved_at',
|
47
|
|
- 'last_sent_at',
|
48
|
|
- 'last_viewed_at',
|
49
|
|
- 'status',
|
50
|
|
- 'currency_code',
|
51
|
|
- 'discount_method',
|
52
|
|
- 'discount_computation',
|
53
|
|
- 'discount_rate',
|
54
|
|
- 'subtotal',
|
55
|
|
- 'tax_total',
|
56
|
|
- 'discount_total',
|
57
|
|
- 'total',
|
58
|
|
- 'amount_used',
|
59
|
|
- 'terms',
|
60
|
|
- 'footer',
|
61
|
|
- 'created_by',
|
62
|
|
- 'updated_by',
|
63
|
|
- ];
|
64
|
|
-
|
65
|
|
- protected $casts = [
|
66
|
|
- 'date' => 'date',
|
67
|
|
- 'approved_at' => 'datetime',
|
68
|
|
- 'last_sent_at' => 'datetime',
|
69
|
|
- 'last_viewed_at' => 'datetime',
|
70
|
|
- 'status' => CreditNoteStatus::class,
|
71
|
|
- 'discount_method' => DocumentDiscountMethod::class,
|
72
|
|
- 'discount_computation' => AdjustmentComputation::class,
|
73
|
|
- 'discount_rate' => RateCast::class,
|
74
|
|
- 'subtotal' => MoneyCast::class,
|
75
|
|
- 'tax_total' => MoneyCast::class,
|
76
|
|
- 'discount_total' => MoneyCast::class,
|
77
|
|
- 'total' => MoneyCast::class,
|
78
|
|
- 'amount_used' => MoneyCast::class,
|
79
|
|
- ];
|
80
|
|
-
|
81
|
|
- // Basic Relationships
|
82
|
|
-
|
83
|
|
- public function client(): BelongsTo
|
84
|
|
- {
|
85
|
|
- return $this->belongsTo(Client::class);
|
86
|
|
- }
|
87
|
|
-
|
88
|
|
- public function company(): BelongsTo
|
89
|
|
- {
|
90
|
|
- return $this->belongsTo(Company::class);
|
91
|
|
- }
|
92
|
|
-
|
93
|
|
- // Transaction Relationships
|
94
|
|
-
|
95
|
|
- public function transactions(): MorphMany
|
96
|
|
- {
|
97
|
|
- return $this->morphMany(Transaction::class, 'transactionable');
|
98
|
|
- }
|
99
|
|
-
|
100
|
|
- public function initialTransaction(): MorphOne
|
101
|
|
- {
|
102
|
|
- return $this->morphOne(Transaction::class, 'transactionable')
|
103
|
|
- ->where('type', TransactionType::Journal);
|
104
|
|
- }
|
105
|
|
-
|
106
|
|
- // Track where this credit note has been applied
|
107
|
|
-
|
108
|
|
- public function applications(): Collection
|
109
|
|
- {
|
110
|
|
- // Find all invoice transactions that reference this credit note
|
111
|
|
- return Transaction::where('type', TransactionType::CreditNote)
|
112
|
|
- ->where('is_payment', true)
|
113
|
|
- ->whereJsonContains('meta->credit_note_id', $this->id)
|
114
|
|
- ->get()
|
115
|
|
- ->map(function ($transaction) {
|
116
|
|
- return [
|
117
|
|
- 'invoice' => $transaction->transactionable,
|
118
|
|
- 'amount' => $transaction->amount,
|
119
|
|
- 'date' => $transaction->posted_at,
|
120
|
|
- 'transaction' => $transaction,
|
121
|
|
- ];
|
122
|
|
- })
|
123
|
|
- ->filter(function ($item) {
|
124
|
|
- return ! is_null($item['invoice']);
|
125
|
|
- });
|
126
|
|
- }
|
127
|
|
-
|
128
|
|
- public function appliedInvoices(): Collection
|
129
|
|
- {
|
130
|
|
- return $this->applications()->pluck('invoice');
|
131
|
|
- }
|
132
|
|
-
|
133
|
|
- // Document Interface Implementation
|
134
|
|
-
|
135
|
|
- public static function documentType(): DocumentType
|
136
|
|
- {
|
137
|
|
- return DocumentType::CreditNote;
|
138
|
|
- }
|
139
|
|
-
|
140
|
|
- public function documentNumber(): ?string
|
141
|
|
- {
|
142
|
|
- return $this->credit_note_number;
|
143
|
|
- }
|
144
|
|
-
|
145
|
|
- public function documentDate(): ?string
|
146
|
|
- {
|
147
|
|
- return $this->date?->toDefaultDateFormat();
|
148
|
|
- }
|
149
|
|
-
|
150
|
|
- public function dueDate(): ?string
|
151
|
|
- {
|
152
|
|
- return null;
|
153
|
|
- }
|
154
|
|
-
|
155
|
|
- public function amountDue(): ?string
|
156
|
|
- {
|
157
|
|
- return null;
|
158
|
|
- }
|
159
|
|
-
|
160
|
|
- public function referenceNumber(): ?string
|
161
|
|
- {
|
162
|
|
- return $this->reference_number;
|
163
|
|
- }
|
164
|
|
-
|
165
|
|
- // Computed Properties
|
166
|
|
-
|
167
|
|
- protected function availableBalance(): Attribute
|
168
|
|
- {
|
169
|
|
- return Attribute::get(function () {
|
170
|
|
- $totalCents = (int) $this->getRawOriginal('total');
|
171
|
|
- $amountUsedCents = (int) $this->getRawOriginal('amount_used');
|
172
|
|
-
|
173
|
|
- return CurrencyConverter::convertCentsToFormatSimple($totalCents - $amountUsedCents);
|
174
|
|
- });
|
175
|
|
- }
|
176
|
|
-
|
177
|
|
- protected function availableBalanceCents(): Attribute
|
178
|
|
- {
|
179
|
|
- return Attribute::get(function () {
|
180
|
|
- $totalCents = (int) $this->getRawOriginal('total');
|
181
|
|
- $amountUsedCents = (int) $this->getRawOriginal('amount_used');
|
182
|
|
-
|
183
|
|
- return $totalCents - $amountUsedCents;
|
184
|
|
- });
|
185
|
|
- }
|
186
|
|
-
|
187
|
|
- // Status Methods
|
188
|
|
-
|
189
|
|
- public function isFullyApplied(): bool
|
190
|
|
- {
|
191
|
|
- return $this->availableBalanceCents <= 0;
|
192
|
|
- }
|
193
|
|
-
|
194
|
|
- public function isPartiallyApplied(): bool
|
195
|
|
- {
|
196
|
|
- $amountUsedCents = (int) $this->getRawOriginal('amount_used');
|
197
|
|
-
|
198
|
|
- return $amountUsedCents > 0 && ! $this->isFullyApplied();
|
199
|
|
- }
|
200
|
|
-
|
201
|
|
- public function isDraft(): bool
|
202
|
|
- {
|
203
|
|
- return $this->status === CreditNoteStatus::Draft;
|
204
|
|
- }
|
205
|
|
-
|
206
|
|
- public function wasApproved(): bool
|
207
|
|
- {
|
208
|
|
- return $this->approved_at !== null;
|
209
|
|
- }
|
210
|
|
-
|
211
|
|
- public function hasBeenSent(): bool
|
212
|
|
- {
|
213
|
|
- return $this->last_sent_at !== null;
|
214
|
|
- }
|
215
|
|
-
|
216
|
|
- public function hasBeenViewed(): bool
|
217
|
|
- {
|
218
|
|
- return $this->last_viewed_at !== null;
|
219
|
|
- }
|
220
|
|
-
|
221
|
|
- public function canBeAppliedToInvoice(): bool
|
222
|
|
- {
|
223
|
|
- return in_array($this->status->value, CreditNoteStatus::canBeApplied()) &&
|
224
|
|
- $this->availableBalanceCents > 0;
|
225
|
|
- }
|
226
|
|
-
|
227
|
|
- // Application Methods
|
228
|
|
-
|
229
|
|
- public function applyToInvoice(Invoice $invoice, string $amount): void
|
230
|
|
- {
|
231
|
|
- // Validate currencies match
|
232
|
|
- if ($this->currency_code !== $invoice->currency_code) {
|
233
|
|
- throw new \RuntimeException('Cannot apply credit note with different currency to invoice.');
|
234
|
|
- }
|
235
|
|
-
|
236
|
|
- // Validate available amount
|
237
|
|
- $amountCents = CurrencyConverter::convertToCents($amount, $this->currency_code);
|
238
|
|
-
|
239
|
|
- if ($amountCents > $this->availableBalanceCents) {
|
240
|
|
- throw new \RuntimeException('Cannot apply more than the available credit note amount.');
|
241
|
|
- }
|
242
|
|
-
|
243
|
|
- // Create transaction on the invoice
|
244
|
|
- $invoice->transactions()->create([
|
245
|
|
- 'company_id' => $this->company_id,
|
246
|
|
- 'type' => TransactionType::CreditNote,
|
247
|
|
- 'is_payment' => true,
|
248
|
|
- 'posted_at' => now(),
|
249
|
|
- 'amount' => $amount,
|
250
|
|
- 'account_id' => Account::getAccountsReceivableAccount()->id,
|
251
|
|
- 'description' => "Credit Note #{$this->credit_note_number} applied to Invoice #{$invoice->invoice_number}",
|
252
|
|
- 'meta' => [
|
253
|
|
- 'credit_note_id' => $this->id,
|
254
|
|
- ],
|
255
|
|
- ]);
|
256
|
|
-
|
257
|
|
- // Update amount used on credit note
|
258
|
|
- $this->amount_used = CurrencyConverter::convertCentsToFormatSimple(
|
259
|
|
- (int) $this->getRawOriginal('amount_used') + $amountCents
|
260
|
|
- );
|
261
|
|
- $this->save();
|
262
|
|
-
|
263
|
|
- // Update status if needed
|
264
|
|
- $this->updateStatusBasedOnUsage();
|
265
|
|
-
|
266
|
|
- // Update invoice payment status
|
267
|
|
- $invoice->updatePaymentStatus();
|
268
|
|
- }
|
269
|
|
-
|
270
|
|
- protected function updateStatusBasedOnUsage(): void
|
271
|
|
- {
|
272
|
|
- if ($this->isFullyApplied()) {
|
273
|
|
- $this->status = CreditNoteStatus::Applied;
|
274
|
|
- } elseif ($this->isPartiallyApplied()) {
|
275
|
|
- $this->status = CreditNoteStatus::Partial;
|
276
|
|
- }
|
277
|
|
-
|
278
|
|
- $this->save();
|
279
|
|
- }
|
280
|
|
-
|
281
|
|
- public function autoApplyToInvoices(): void
|
282
|
|
- {
|
283
|
|
- // Skip if no available amount
|
284
|
|
- if ($this->availableBalanceCents <= 0) {
|
285
|
|
- return;
|
286
|
|
- }
|
287
|
|
-
|
288
|
|
- // Find unpaid invoices for this client, ordered by due date (oldest first)
|
289
|
|
- $unpaidInvoices = Invoice::where('client_id', $this->client_id)
|
290
|
|
- ->where('currency_code', $this->currency_code)
|
291
|
|
- ->unpaid()
|
292
|
|
- ->orderBy('due_date')
|
293
|
|
- ->get();
|
294
|
|
-
|
295
|
|
- // Apply to invoices until amount is used up
|
296
|
|
- foreach ($unpaidInvoices as $invoice) {
|
297
|
|
- $invoiceAmountDueCents = (int) $invoice->getRawOriginal('amount_due');
|
298
|
|
-
|
299
|
|
- if ($invoiceAmountDueCents <= 0 || $this->availableBalanceCents <= 0) {
|
300
|
|
- continue;
|
301
|
|
- }
|
302
|
|
-
|
303
|
|
- // Calculate amount to apply to this invoice
|
304
|
|
- $applyAmountCents = min($this->availableBalanceCents, $invoiceAmountDueCents);
|
305
|
|
- $applyAmount = CurrencyConverter::convertCentsToFormatSimple($applyAmountCents);
|
306
|
|
-
|
307
|
|
- // Apply to invoice
|
308
|
|
- $this->applyToInvoice($invoice, $applyAmount);
|
309
|
|
-
|
310
|
|
- if ($this->availableBalanceCents <= 0) {
|
311
|
|
- break;
|
312
|
|
- }
|
313
|
|
- }
|
314
|
|
- }
|
315
|
|
-
|
316
|
|
- // Accounting Methods
|
317
|
|
-
|
318
|
|
- public function createInitialTransaction(?Carbon $postedAt = null): void
|
319
|
|
- {
|
320
|
|
- $postedAt ??= $this->date;
|
321
|
|
-
|
322
|
|
- $total = $this->formatAmountToDefaultCurrency($this->getRawOriginal('total'));
|
323
|
|
-
|
324
|
|
- $transaction = $this->transactions()->create([
|
325
|
|
- 'company_id' => $this->company_id,
|
326
|
|
- 'type' => TransactionType::Journal,
|
327
|
|
- 'posted_at' => $postedAt,
|
328
|
|
- 'amount' => $total,
|
329
|
|
- 'description' => 'Credit Note Creation for Credit Note #' . $this->credit_note_number,
|
330
|
|
- ]);
|
331
|
|
-
|
332
|
|
- $baseDescription = "{$this->client->name}: Credit Note #{$this->credit_note_number}";
|
333
|
|
-
|
334
|
|
- // Credit AR (opposite of invoice)
|
335
|
|
- $transaction->journalEntries()->create([
|
336
|
|
- 'company_id' => $this->company_id,
|
337
|
|
- 'type' => JournalEntryType::Credit,
|
338
|
|
- 'account_id' => Account::getAccountsReceivableAccount()->id,
|
339
|
|
- 'amount' => $total,
|
340
|
|
- 'description' => $baseDescription,
|
341
|
|
- ]);
|
342
|
|
-
|
343
|
|
- // Handle line items - debit revenue accounts (reverse of invoice)
|
344
|
|
- foreach ($this->lineItems as $lineItem) {
|
345
|
|
- $lineItemDescription = "{$baseDescription} › {$lineItem->offering->name}";
|
346
|
|
- $lineItemSubtotal = $this->formatAmountToDefaultCurrency($lineItem->getRawOriginal('subtotal'));
|
347
|
|
-
|
348
|
|
- $transaction->journalEntries()->create([
|
349
|
|
- 'company_id' => $this->company_id,
|
350
|
|
- 'type' => JournalEntryType::Debit,
|
351
|
|
- 'account_id' => $lineItem->offering->income_account_id,
|
352
|
|
- 'amount' => $lineItemSubtotal,
|
353
|
|
- 'description' => $lineItemDescription,
|
354
|
|
- ]);
|
355
|
|
-
|
356
|
|
- // Handle adjustments
|
357
|
|
- foreach ($lineItem->adjustments as $adjustment) {
|
358
|
|
- $adjustmentAmount = $this->formatAmountToDefaultCurrency($lineItem->calculateAdjustmentTotalAmount($adjustment));
|
359
|
|
-
|
360
|
|
- $transaction->journalEntries()->create([
|
361
|
|
- 'company_id' => $this->company_id,
|
362
|
|
- 'type' => $adjustment->category->isDiscount() ? JournalEntryType::Credit : JournalEntryType::Debit,
|
363
|
|
- 'account_id' => $adjustment->account_id,
|
364
|
|
- 'amount' => $adjustmentAmount,
|
365
|
|
- 'description' => $lineItemDescription,
|
366
|
|
- ]);
|
367
|
|
- }
|
368
|
|
- }
|
369
|
|
- }
|
370
|
|
-
|
371
|
|
- public function approveDraft(?Carbon $approvedAt = null): void
|
372
|
|
- {
|
373
|
|
- if (! $this->isDraft()) {
|
374
|
|
- throw new \RuntimeException('Credit note is not in draft status.');
|
375
|
|
- }
|
376
|
|
-
|
377
|
|
- $this->createInitialTransaction();
|
378
|
|
-
|
379
|
|
- $approvedAt ??= now();
|
380
|
|
-
|
381
|
|
- $this->update([
|
382
|
|
- 'approved_at' => $approvedAt,
|
383
|
|
- 'status' => CreditNoteStatus::Sent,
|
384
|
|
- ]);
|
385
|
|
-
|
386
|
|
- // Auto-apply if configured in settings
|
387
|
|
- if ($this->company->settings->auto_apply_credit_notes ?? true) {
|
388
|
|
- $this->autoApplyToInvoices();
|
389
|
|
- }
|
390
|
|
- }
|
391
|
|
-
|
392
|
|
- public function convertAmountToDefaultCurrency(int $amountCents): int
|
393
|
|
- {
|
394
|
|
- $defaultCurrency = CurrencyAccessor::getDefaultCurrency();
|
395
|
|
- $needsConversion = $this->currency_code !== $defaultCurrency;
|
396
|
|
-
|
397
|
|
- if ($needsConversion) {
|
398
|
|
- return CurrencyConverter::convertBalance($amountCents, $this->currency_code, $defaultCurrency);
|
399
|
|
- }
|
400
|
|
-
|
401
|
|
- return $amountCents;
|
402
|
|
- }
|
403
|
|
-
|
404
|
|
- public function formatAmountToDefaultCurrency(int $amountCents): string
|
405
|
|
- {
|
406
|
|
- $convertedCents = $this->convertAmountToDefaultCurrency($amountCents);
|
407
|
|
-
|
408
|
|
- return CurrencyConverter::convertCentsToFormatSimple($convertedCents);
|
409
|
|
- }
|
410
|
|
-
|
411
|
|
- // Other methods
|
412
|
|
-
|
413
|
|
- public function markAsSent(?Carbon $sentAt = null): void
|
414
|
|
- {
|
415
|
|
- $sentAt ??= now();
|
416
|
|
-
|
417
|
|
- $this->update([
|
418
|
|
- 'status' => CreditNoteStatus::Sent,
|
419
|
|
- 'last_sent_at' => $sentAt,
|
420
|
|
- ]);
|
421
|
|
- }
|
422
|
|
-
|
423
|
|
- public function markAsViewed(?Carbon $viewedAt = null): void
|
424
|
|
- {
|
425
|
|
- $viewedAt ??= now();
|
426
|
|
-
|
427
|
|
- $this->update([
|
428
|
|
- 'status' => CreditNoteStatus::Viewed,
|
429
|
|
- 'last_viewed_at' => $viewedAt,
|
430
|
|
- ]);
|
431
|
|
- }
|
432
|
|
-
|
433
|
|
- // Utility Methods
|
434
|
|
-
|
435
|
|
- public static function getNextDocumentNumber(?Company $company = null): string
|
436
|
|
- {
|
437
|
|
- $company ??= auth()->user()?->currentCompany;
|
438
|
|
-
|
439
|
|
- if (! $company) {
|
440
|
|
- throw new \RuntimeException('No current company is set for the user.');
|
441
|
|
- }
|
442
|
|
-
|
443
|
|
- $defaultSettings = $company->defaultCreditNote;
|
444
|
|
-
|
445
|
|
- $numberPrefix = $defaultSettings->number_prefix ?? 'CN-';
|
446
|
|
-
|
447
|
|
- $latestDocument = static::query()
|
448
|
|
- ->whereNotNull('credit_note_number')
|
449
|
|
- ->latest('credit_note_number')
|
450
|
|
- ->first();
|
451
|
|
-
|
452
|
|
- $lastNumberNumericPart = $latestDocument
|
453
|
|
- ? (int) substr($latestDocument->credit_note_number, strlen($numberPrefix))
|
454
|
|
- : DocumentDefault::getBaseNumber();
|
455
|
|
-
|
456
|
|
- $numberNext = $lastNumberNumericPart + 1;
|
457
|
|
-
|
458
|
|
- return $defaultSettings->getNumberNext(
|
459
|
|
- prefix: $numberPrefix,
|
460
|
|
- next: $numberNext
|
461
|
|
- );
|
462
|
|
- }
|
463
|
|
-
|
464
|
|
- // Action Methods
|
465
|
|
-
|
466
|
|
- public static function getReplicateAction(string $action = ReplicateAction::class): MountableAction
|
467
|
|
- {
|
468
|
|
- return $action::make()
|
469
|
|
- ->excludeAttributes([
|
470
|
|
- 'status',
|
471
|
|
- 'amount_used',
|
472
|
|
- 'created_by',
|
473
|
|
- 'updated_by',
|
474
|
|
- 'created_at',
|
475
|
|
- 'updated_at',
|
476
|
|
- 'credit_note_number',
|
477
|
|
- 'date',
|
478
|
|
- 'approved_at',
|
479
|
|
- 'last_sent_at',
|
480
|
|
- 'last_viewed_at',
|
481
|
|
- ])
|
482
|
|
- ->modal(false)
|
483
|
|
- ->beforeReplicaSaved(function (self $original, self $replica) {
|
484
|
|
- $replica->status = CreditNoteStatus::Draft;
|
485
|
|
- $replica->credit_note_number = self::getNextDocumentNumber();
|
486
|
|
- $replica->date = now();
|
487
|
|
- $replica->amount_used = 0;
|
488
|
|
- })
|
489
|
|
- ->databaseTransaction()
|
490
|
|
- ->after(function (self $original, self $replica) {
|
491
|
|
- $original->replicateLineItems($replica);
|
492
|
|
- })
|
493
|
|
- ->successRedirectUrl(static function (self $replica) {
|
494
|
|
- return CreditNoteResource::getUrl('edit', ['record' => $replica]);
|
495
|
|
- });
|
496
|
|
- }
|
497
|
|
-
|
498
|
|
- public static function getApproveAction(string $action = Action::class): MountableAction
|
499
|
|
- {
|
500
|
|
- return $action::make('approve')
|
501
|
|
- ->label('Approve')
|
502
|
|
- ->icon('heroicon-m-check-circle')
|
503
|
|
- ->visible(fn (self $record) => $record->isDraft())
|
504
|
|
- ->requiresConfirmation()
|
505
|
|
- ->databaseTransaction()
|
506
|
|
- ->successNotificationTitle('Credit note approved')
|
507
|
|
- ->action(function (self $record) {
|
508
|
|
- $record->approveDraft();
|
509
|
|
- });
|
510
|
|
- }
|
511
|
|
-
|
512
|
|
- public function replicateLineItems(Model $target): void
|
513
|
|
- {
|
514
|
|
- $this->lineItems->each(function (DocumentLineItem $lineItem) use ($target) {
|
515
|
|
- $replica = $lineItem->replicate([
|
516
|
|
- 'documentable_id',
|
517
|
|
- 'documentable_type',
|
518
|
|
- 'subtotal',
|
519
|
|
- 'total',
|
520
|
|
- 'created_by',
|
521
|
|
- 'updated_by',
|
522
|
|
- 'created_at',
|
523
|
|
- 'updated_at',
|
524
|
|
- ]);
|
525
|
|
-
|
526
|
|
- $replica->documentable_id = $target->id;
|
527
|
|
- $replica->documentable_type = $target->getMorphClass();
|
528
|
|
- $replica->save();
|
529
|
|
-
|
530
|
|
- $replica->adjustments()->sync($lineItem->adjustments->pluck('id'));
|
531
|
|
- });
|
532
|
|
- }
|
533
|
|
-}
|