From 7e914911459c901ca5c9c9cab6c0299a64309d22 Mon Sep 17 00:00:00 2001 From: denisdulici Date: Sat, 7 Dec 2019 12:54:13 +0300 Subject: [PATCH] improved invoice/bill jobs --- app/Events/Expense/BillCreating.php | 22 +++ app/Events/Expense/BillUpdating.php | 26 ++++ app/Http/Controllers/Common/Items.php | 31 ++-- app/Jobs/Expense/CreateBill.php | 197 +++++++++++++----------- app/Jobs/Expense/CreateBillItem.php | 71 ++++----- app/Jobs/Expense/UpdateBill.php | 209 ++++++++++++------------- app/Jobs/Income/CreateInvoice.php | 190 ++++++++++++----------- app/Jobs/Income/CreateInvoiceItem.php | 69 ++++----- app/Jobs/Income/UpdateInvoice.php | 211 ++++++++++++-------------- 9 files changed, 515 insertions(+), 511 deletions(-) create mode 100644 app/Events/Expense/BillCreating.php create mode 100644 app/Events/Expense/BillUpdating.php diff --git a/app/Events/Expense/BillCreating.php b/app/Events/Expense/BillCreating.php new file mode 100644 index 000000000..7eaef525d --- /dev/null +++ b/app/Events/Expense/BillCreating.php @@ -0,0 +1,22 @@ +request = $request; + } +} diff --git a/app/Events/Expense/BillUpdating.php b/app/Events/Expense/BillUpdating.php new file mode 100644 index 000000000..e296ca695 --- /dev/null +++ b/app/Events/Expense/BillUpdating.php @@ -0,0 +1,26 @@ +bill = $bill; + $this->request = $request; + } +} diff --git a/app/Http/Controllers/Common/Items.php b/app/Http/Controllers/Common/Items.php index 94b0b6f4c..66636856f 100644 --- a/app/Http/Controllers/Common/Items.php +++ b/app/Http/Controllers/Common/Items.php @@ -291,12 +291,12 @@ class Items extends Controller $currency_code = setting('default.currency'); } - $json = new \stdClass; + $json = new \stdClass(); $sub_total = 0; $tax_total = 0; - $items = array(); + $items = []; if ($input_items) { foreach ($input_items as $key => $item) { @@ -307,15 +307,15 @@ class Items extends Controller $item_tax_amount = 0; $item_sub_total = ($price * $quantity); - $item_discount_total = $item_sub_total; + $item_discounted_total = $item_sub_total; - // Apply discount to item + // Apply discount to amount if ($discount) { - $item_discount_total = $item_sub_total - ($item_sub_total * ($discount / 100)); + $item_discounted_total = $item_sub_total - ($item_sub_total * ($discount / 100)); } if (!empty($item['tax_id'])) { - $inclusives = $compounds = $taxes = $fixed_taxes = []; + $inclusives = $compounds = []; foreach ($item['tax_id'] as $tax_id) { $tax = Tax::find($tax_id); @@ -328,15 +328,10 @@ class Items extends Controller $compounds[] = $tax; break; case 'fixed': - $fixed_taxes[] = $tax; - - $item_tax_total += $tax->rate; + $item_tax_total += $tax->rate * $quantity; break; - case 'normal': default: - $taxes[] = $tax; - - $item_tax_amount = ($item_discount_total / 100) * $tax->rate; + $item_tax_amount = ($item_discounted_total / 100) * $tax->rate; $item_tax_total += $item_tax_amount; break; @@ -344,9 +339,9 @@ class Items extends Controller } if ($inclusives) { - $item_sub_and_tax_total = $item_discount_total + $item_tax_total; + $item_sub_and_tax_total = $item_discounted_total + $item_tax_total; - $item_base_rate = $item_sub_and_tax_total / (1 + collect($inclusives)->sum('rate')/100); + $item_base_rate = $item_sub_and_tax_total / (1 + collect($inclusives)->sum('rate') / 100); $item_tax_total = $item_sub_and_tax_total - $item_base_rate; $item_sub_total = $item_base_rate + $discount; @@ -354,7 +349,7 @@ class Items extends Controller if ($compounds) { foreach ($compounds as $compound) { - $item_tax_total += (($item_discount_total + $item_tax_total) / 100) * $compound->rate; + $item_tax_total += (($item_discounted_total + $item_tax_total) / 100) * $compound->rate; } } } @@ -370,14 +365,14 @@ class Items extends Controller $json->sub_total = money($sub_total, $currency_code, true)->format(); - $json->discount_text= trans('invoices.add_discount'); + $json->discount_text = trans('invoices.add_discount'); $json->discount_total = ''; $json->tax_total = money($tax_total, $currency_code, true)->format(); // Apply discount to total if ($discount) { - $json->discount_text= trans('invoices.show_discount', ['discount' => $discount]); + $json->discount_text = trans('invoices.show_discount', ['discount' => $discount]); $json->discount_total = money($sub_total * ($discount / 100), $currency_code, true)->format(); $sub_total = $sub_total - ($sub_total * ($discount / 100)); diff --git a/app/Jobs/Expense/CreateBill.php b/app/Jobs/Expense/CreateBill.php index 7ca8c7e54..5f651219b 100644 --- a/app/Jobs/Expense/CreateBill.php +++ b/app/Jobs/Expense/CreateBill.php @@ -4,6 +4,7 @@ namespace App\Jobs\Expense; use App\Abstracts\Job; use App\Events\Expense\BillCreated; +use App\Events\Expense\BillCreating; use App\Models\Expense\Bill; use App\Models\Expense\BillTotal; use App\Traits\Currencies; @@ -15,6 +16,8 @@ class CreateBill extends Job protected $request; + protected $bill; + /** * Create a new job instance. * @@ -28,153 +31,165 @@ class CreateBill extends Job /** * Execute the job. * - * @return Invoice + * @return Bill */ public function handle() { - $bill = Bill::create($this->request->all()); + if (empty($this->request['amount'])) { + $this->request['amount'] = 0; + } + + event(new BillCreating($this->request)); + + $this->bill = Bill::create($this->request->all()); // Upload attachment if ($this->request->file('attachment')) { $media = $this->getMedia($this->request->file('attachment'), 'bills'); - $bill->attachMedia($media, 'attachment'); + $this->bill->attachMedia($media, 'attachment'); } - $taxes = []; + $this->createItemsAndTotals(); - $tax_total = 0; - $sub_total = 0; - $discount_total = 0; - $discount = $this->request['discount']; + $this->bill->update($this->request->input()); - if ($this->request['items']) { - foreach ($this->request['items'] as $item) { - $bill_item = $this->dispatch(new CreateBillItem($item, $bill, $discount)); + $this->bill->createRecurring(); - // Calculate totals - $tax_total += $bill_item->tax; - $sub_total += $bill_item->total; + event(new BillCreated($this->bill)); - // Set taxes - if ($bill_item->item_taxes) { - foreach ($bill_item->item_taxes as $item_tax) { - if (isset($taxes) && array_key_exists($item_tax['tax_id'], $taxes)) { - $taxes[$item_tax['tax_id']]['amount'] += $item_tax['amount']; - } else { - $taxes[$item_tax['tax_id']] = [ - 'name' => $item_tax['name'], - 'amount' => $item_tax['amount'] - ]; - } - } - } - } - } - - $s_total = $sub_total; - - // Apply discount to total - if ($discount) { - $s_discount = $s_total * ($discount / 100); - $discount_total += $s_discount; - $s_total = $s_total - $s_discount; - } - - $amount = $s_total + $tax_total; - - $this->request['amount'] = money($amount, $this->request['currency_code'])->getAmount(); - - $bill->update($this->request->input()); - - // Add bill totals - $this->addTotals($bill, $this->request, $taxes, $sub_total, $discount_total, $tax_total); - - // Recurring - $bill->createRecurring(); - - // Fire the event to make it extendible - event(new BillCreated($bill)); - - return $bill; + return $this->bill; } - protected function addTotals($bill, $request, $taxes, $sub_total, $discount_total, $tax_total) + protected function createItemsAndTotals() { - // Check if totals are in request, i.e. api - if (!empty($request['totals'])) { - $sort_order = 1; - - foreach ($request['totals'] as $total) { - $total['bill_id'] = $bill->id; - - if (empty($total['sort_order'])) { - $total['sort_order'] = $sort_order; - } - - BillTotal::create($total); - - $sort_order++; - } - - return; - } + // Create items + list($sub_total, $taxes) = $this->createItems(); $sort_order = 1; - // Added bill sub total + // Add sub total BillTotal::create([ - 'company_id' => $request['company_id'], - 'bill_id' => $bill->id, + 'company_id' => $this->bill->company_id, + 'bill_id' => $this->bill->id, 'code' => 'sub_total', 'name' => 'bills.sub_total', 'amount' => $sub_total, 'sort_order' => $sort_order, ]); + $this->request['amount'] += $sub_total; + $sort_order++; - // Added bill discount - if ($discount_total) { + // Add discount + if (!empty($this->request['discount'])) { + $discount_total = $sub_total * ($this->request['discount'] / 100); + BillTotal::create([ - 'company_id' => $request['company_id'], - 'bill_id' => $bill->id, + 'company_id' => $this->bill->company_id, + 'bill_id' => $this->bill->id, 'code' => 'discount', 'name' => 'bills.discount', 'amount' => $discount_total, 'sort_order' => $sort_order, ]); - // This is for total - $sub_total = $sub_total - $discount_total; + $this->request['amount'] -= $discount_total; $sort_order++; } - // Added bill taxes - if (isset($taxes)) { + // Add taxes + if (!empty($taxes)) { foreach ($taxes as $tax) { BillTotal::create([ - 'company_id' => $request['company_id'], - 'bill_id' => $bill->id, + 'company_id' => $this->bill->company_id, + 'bill_id' => $this->bill->id, 'code' => 'tax', 'name' => $tax['name'], 'amount' => $tax['amount'], 'sort_order' => $sort_order, ]); + $this->request['amount'] += $tax['amount']; + $sort_order++; } } - // Added bill total + // Add extra totals, i.e. shipping fee + if (!empty($this->request['totals'])) { + foreach ($this->request['totals'] as $total) { + $total['company_id'] = $this->bill->company_id; + $total['bill_id'] = $this->bill->id; + $total['sort_order'] = $sort_order; + + if (empty($total['code'])) { + $total['code'] = 'extra'; + } + + BillTotal::create($total); + + if (empty($total['operator']) || ($total['operator'] == 'addition')) { + $this->request['amount'] += $total['amount']; + } else { + // subtraction + $this->request['amount'] -= $total['amount']; + } + + $sort_order++; + } + } + + // Add total BillTotal::create([ - 'company_id' => $request['company_id'], - 'bill_id' => $bill->id, + 'company_id' => $this->bill->company_id, + 'bill_id' => $this->bill->id, 'code' => 'total', 'name' => 'bills.total', - 'amount' => $sub_total + $tax_total, + 'amount' => $this->request['amount'], 'sort_order' => $sort_order, ]); } + + protected function createItems() + { + $sub_total = 0; + + $taxes = []; + + if (empty($this->request['items'])) { + return [$sub_total, $taxes]; + } + + foreach ((array) $this->request['items'] as $item) { + if (empty($item['discount'])) { + $item['discount'] = !empty($this->request['discount']) ? !empty($this->request['discount']) : 0; + } + + $bill_item = $this->dispatch(new CreateBillItem($item, $this->bill)); + + // Calculate totals + $sub_total += $bill_item->total; + + if (!$bill_item->item_taxes) { + continue; + } + + // Set taxes + foreach ((array) $bill_item->item_taxes as $item_tax) { + if (array_key_exists($item_tax['tax_id'], $taxes)) { + $taxes[$item_tax['tax_id']]['amount'] += $item_tax['amount']; + } else { + $taxes[$item_tax['tax_id']] = [ + 'name' => $item_tax['name'], + 'amount' => $item_tax['amount'] + ]; + } + } + } + + return [$sub_total, $taxes]; + } } diff --git a/app/Jobs/Expense/CreateBillItem.php b/app/Jobs/Expense/CreateBillItem.php index 6d5b84b3c..ed7167cc1 100644 --- a/app/Jobs/Expense/CreateBillItem.php +++ b/app/Jobs/Expense/CreateBillItem.php @@ -10,24 +10,20 @@ use Illuminate\Support\Str; class CreateBillItem extends Job { - protected $data; + protected $request; protected $bill; - protected $discount; - /** * Create a new job instance. * - * @param $data + * @param $request * @param $bill - * @param $discount */ - public function __construct($data, $bill, $discount = null) + public function __construct($request, $bill) { - $this->data = $data; + $this->request = $request; $this->bill = $bill; - $this->discount = $discount; } /** @@ -37,36 +33,37 @@ class CreateBillItem extends Job */ public function handle() { - $item_id = !empty($this->data['item_id']) ? $this->data['item_id'] : 0; - $item_amount = (double) $this->data['price'] * (double) $this->data['quantity']; + $item_id = !empty($this->request['item_id']) ? $this->request['item_id'] : 0; + $item_amount = (double) $this->request['price'] * (double) $this->request['quantity']; - $item_discount_amount = $item_amount; + $item_discounted_amount = $item_amount; - // Apply discount to tax - if ($this->discount) { - $item_discount_amount = $item_amount - ($item_amount * ($this->discount / 100)); + // Apply discount to amount + if (!empty($this->request['discount'])) { + $item_discounted_amount = $item_amount - ($item_amount * ($this->request['discount'] / 100)); } $tax_amount = 0; $item_taxes = []; $item_tax_total = 0; - if (!empty($this->data['tax_id'])) { - $inclusives = $compounds = $taxes = []; + if (!empty($this->request['tax_id'])) { + $inclusives = $compounds = []; - foreach ((array) $this->data['tax_id'] as $tax_id) { + foreach ((array) $this->request['tax_id'] as $tax_id) { $tax = Tax::find($tax_id); switch ($tax->type) { case 'inclusive': $inclusives[] = $tax; + break; case 'compound': $compounds[] = $tax; + break; case 'fixed': - $fixed_taxes[] = $tax; - $tax_amount = $tax->rate; + $tax_amount = $tax->rate * (double) $this->request['quantity']; $item_taxes[] = [ 'company_id' => $this->invoice->company_id, @@ -77,12 +74,10 @@ class CreateBillItem extends Job ]; $item_tax_total += $tax_amount; - break; - case 'normal': - default: - $taxes[] = $tax; - $tax_amount = ($item_discount_amount / 100) * $tax->rate; + break; + default: + $tax_amount = ($item_discounted_amount / 100) * $tax->rate; $item_taxes[] = [ 'company_id' => $this->bill->company_id, @@ -93,12 +88,13 @@ class CreateBillItem extends Job ]; $item_tax_total += $tax_amount; + break; } } if ($inclusives) { - $item_amount = $item_discount_amount + $item_tax_total; + $item_amount = $item_discounted_amount + $item_tax_total; $item_base_rate = $item_amount / (1 + collect($inclusives)->sum('rate') / 100); @@ -114,12 +110,12 @@ class CreateBillItem extends Job ]; } - $item_amount = ($item_amount - $item_tax_total) / (1 - $this->discount / 100); + $item_amount = ($item_amount - $item_tax_total) / (1 - $this->request['discount'] / 100); } if ($compounds) { foreach ($compounds as $compound) { - $tax_amount = (($item_discount_amount + $item_tax_total) / 100) * $compound->rate; + $tax_amount = (($item_discounted_amount + $item_tax_total) / 100) * $compound->rate; $item_tax_total += $tax_amount; @@ -138,9 +134,9 @@ class CreateBillItem extends Job 'company_id' => $this->bill->company_id, 'bill_id' => $this->bill->id, 'item_id' => $item_id, - 'name' => Str::limit($this->data['name'], 180, ''), - 'quantity' => (double) $this->data['quantity'], - 'price' => (double) $this->data['price'], + 'name' => Str::limit($this->request['name'], 180, ''), + 'quantity' => (double) $this->request['quantity'], + 'price' => (double) $this->request['price'], 'tax' => $item_tax_total, 'total' => $item_amount, ]); @@ -149,28 +145,15 @@ class CreateBillItem extends Job $bill_item->inclusives = false; $bill_item->compounds = false; - // set item_taxes for - if (!empty($this->data['tax_id'])) { + if (!empty($item_taxes)) { $bill_item->item_taxes = $item_taxes; $bill_item->inclusives = $inclusives; $bill_item->compounds = $compounds; - } - if ($item_taxes) { foreach ($item_taxes as $item_tax) { $item_tax['bill_item_id'] = $bill_item->id; BillItemTax::create($item_tax); - - // Set taxes - if (isset($taxes) && array_key_exists($item_tax['tax_id'], $taxes)) { - $taxes[$item_tax['tax_id']]['amount'] += $item_tax['amount']; - } else { - $taxes[$item_tax['tax_id']] = [ - 'name' => $item_tax['name'], - 'amount' => $item_tax['amount'] - ]; - } } } diff --git a/app/Jobs/Expense/UpdateBill.php b/app/Jobs/Expense/UpdateBill.php index a17bf0c4b..64d512f8c 100644 --- a/app/Jobs/Expense/UpdateBill.php +++ b/app/Jobs/Expense/UpdateBill.php @@ -3,16 +3,17 @@ namespace App\Jobs\Expense; use App\Abstracts\Job; -use App\Models\Common\Item; +use App\Events\Expense\BillUpdated; +use App\Events\Expense\BillUpdating; use App\Models\Expense\Bill; use App\Models\Expense\BillTotal; use App\Traits\Currencies; use App\Traits\DateTime; -use Illuminate\Database\Eloquent\Collection; +use App\Traits\Relationships; class UpdateBill extends Job { - use Currencies, DateTime; + use Currencies, DateTime, Relationships; protected $bill; @@ -36,6 +37,12 @@ class UpdateBill extends Job */ public function handle() { + if (empty($this->request['amount'])) { + $this->request['amount'] = 0; + } + + event(new BillUpdating($this->bill, $this->request)); + // Upload attachment if ($this->request->file('attachment')) { $media = $this->getMedia($this->request->file('attachment'), 'bills'); @@ -43,51 +50,7 @@ class UpdateBill extends Job $this->bill->attachMedia($media, 'attachment'); } - $taxes = []; - - $tax_total = 0; - $sub_total = 0; - $discount_total = 0; - $discount = $this->request['discount']; - - if ($this->request['items']) { - $this->deleteRelationships($this->bill, ['items', 'item_taxes']); - - foreach ($this->request['items'] as $item) { - $bill_item = dispatch_now(new CreateBillItem($item, $this->bill, $discount)); - - // Calculate totals - $tax_total += $bill_item->tax; - $sub_total += $bill_item->total; - - // Set taxes - if ($bill_item->item_taxes) { - foreach ($bill_item->item_taxes as $item_tax) { - if (isset($taxes) && array_key_exists($item_tax['tax_id'], $taxes)) { - $taxes[$item_tax['tax_id']]['amount'] += $item_tax['amount']; - } else { - $taxes[$item_tax['tax_id']] = [ - 'name' => $item_tax['name'], - 'amount' => $item_tax['amount'] - ]; - } - } - } - } - } - - $s_total = $sub_total; - - // Apply discount to total - if ($discount) { - $s_discount = $s_total * ($discount / 100); - $discount_total += $s_discount; - $s_total = $s_total - $s_discount; - } - - $amount = $s_total + $tax_total; - - $this->request['amount'] = money($amount, $this->request['currency_code'])->getAmount(); + $this->createItemsAndTotals(); $bill_paid = $this->bill->paid; @@ -99,124 +62,148 @@ class UpdateBill extends Job $this->bill->update($this->request->input()); - // Delete previous bill totals - $this->deleteRelationships($this->bill, 'totals'); - - // Add bill totals - $this->addTotals($this->bill, $this->request, $taxes, $sub_total, $discount_total, $tax_total); - - // Recurring $this->bill->updateRecurring(); - // Fire the event to make it extensible - event(new \App\Events\Expense\BillUpdated($this->bill)); + event(new BillUpdated($this->bill)); return $this->bill; } - protected function addTotals($bill, $request, $taxes, $sub_total, $discount_total, $tax_total) + protected function createItemsAndTotals() { - // Check if totals are in request, i.e. api - if (!empty($request['totals'])) { - $sort_order = 1; + // Create items + list($sub_total, $taxes) = $this->createItems(); - foreach ($request['totals'] as $total) { - $total['bill_id'] = $bill->id; - - if (empty($total['sort_order'])) { - $total['sort_order'] = $sort_order; - } - - BillTotal::create($total); - - $sort_order++; - } - - return; - } + // Delete current totals + $this->deleteRelationships($this->bill, 'totals'); $sort_order = 1; - // Added bill sub total + // Add sub total BillTotal::create([ - 'company_id' => $request['company_id'], - 'bill_id' => $bill->id, + 'company_id' => $this->bill->company_id, + 'bill_id' => $this->bill->id, 'code' => 'sub_total', 'name' => 'bills.sub_total', 'amount' => $sub_total, 'sort_order' => $sort_order, ]); + $this->request['amount'] += $sub_total; + $sort_order++; - // Added bill discount - if ($discount_total) { + // Add discount + if (!empty($this->request['discount'])) { + $discount_total = $sub_total * ($this->request['discount'] / 100); + BillTotal::create([ - 'company_id' => $request['company_id'], - 'bill_id' => $bill->id, + 'company_id' => $this->bill->company_id, + 'bill_id' => $this->bill->id, 'code' => 'discount', 'name' => 'bills.discount', 'amount' => $discount_total, 'sort_order' => $sort_order, ]); - // This is for total - $sub_total = $sub_total - $discount_total; + $this->request['amount'] -= $discount_total; $sort_order++; } - // Added bill taxes - if (isset($taxes)) { + // Add taxes + if (!empty($taxes)) { foreach ($taxes as $tax) { BillTotal::create([ - 'company_id' => $request['company_id'], - 'bill_id' => $bill->id, + 'company_id' => $this->bill->company_id, + 'bill_id' => $this->bill->id, 'code' => 'tax', 'name' => $tax['name'], 'amount' => $tax['amount'], 'sort_order' => $sort_order, ]); + $this->request['amount'] += $tax['amount']; + $sort_order++; } } - // Added bill total + // Add extra totals, i.e. shipping fee + if (!empty($this->request['totals'])) { + foreach ($this->request['totals'] as $total) { + $total['company_id'] = $this->bill->company_id; + $total['bill_id'] = $this->bill->id; + $total['sort_order'] = $sort_order; + + if (empty($total['code'])) { + $total['code'] = 'extra'; + } + + BillTotal::create($total); + + if (empty($total['operator']) || ($total['operator'] == 'addition')) { + $this->request['amount'] += $total['amount']; + } else { + // subtraction + $this->request['amount'] -= $total['amount']; + } + + $sort_order++; + } + } + + // Add total BillTotal::create([ - 'company_id' => $request['company_id'], - 'bill_id' => $bill->id, + 'company_id' => $this->bill->company_id, + 'bill_id' => $this->bill->id, 'code' => 'total', 'name' => 'bills.total', - 'amount' => $sub_total + $tax_total, + 'amount' => $this->request['amount'], 'sort_order' => $sort_order, ]); } - /** - * Mass delete relationships with events being fired. - * - * @param $model - * @param $relationships - * - * @return void - */ - public function deleteRelationships($model, $relationships) + protected function createItems() { - foreach ((array) $relationships as $relationship) { - if (empty($model->$relationship)) { + $sub_total = 0; + + $taxes = []; + + if (empty($this->request['items'])) { + return [$sub_total, $taxes]; + } + + // Delete current items + $this->deleteRelationships($this->bill, ['items', 'item_taxes']); + + foreach ((array) $this->request['items'] as $item) { + if (empty($item['discount'])) { + $item['discount'] = !empty($this->request['discount']) ? !empty($this->request['discount']) : 0; + } + + $bill_item = $this->dispatch(new CreateBillItem($item, $this->bill)); + + // Calculate totals + $sub_total += $bill_item->total; + + if (!$bill_item->item_taxes) { continue; } - $items = $model->$relationship->all(); - - if ($items instanceof Collection) { - $items = $items->all(); - } - - foreach ((array) $items as $item) { - $item->delete(); + // Set taxes + foreach ((array) $bill_item->item_taxes as $item_tax) { + if (array_key_exists($item_tax['tax_id'], $taxes)) { + $taxes[$item_tax['tax_id']]['amount'] += $item_tax['amount']; + } else { + $taxes[$item_tax['tax_id']] = [ + 'name' => $item_tax['name'], + 'amount' => $item_tax['amount'] + ]; + } } } + + return [$sub_total, $taxes]; } } diff --git a/app/Jobs/Income/CreateInvoice.php b/app/Jobs/Income/CreateInvoice.php index d43ec03b0..9a213f9c9 100644 --- a/app/Jobs/Income/CreateInvoice.php +++ b/app/Jobs/Income/CreateInvoice.php @@ -9,14 +9,15 @@ use App\Models\Income\Invoice; use App\Models\Income\InvoiceTotal; use App\Traits\Currencies; use App\Traits\DateTime; -use App\Traits\Incomes; class CreateInvoice extends Job { - use Currencies, DateTime, Incomes; + use Currencies, DateTime; protected $request; + protected $invoice; + /** * Create a new job instance. * @@ -40,148 +41,155 @@ class CreateInvoice extends Job event(new InvoiceCreating($this->request)); - $invoice = Invoice::create($this->request->all()); + $this->invoice = Invoice::create($this->request->all()); // Upload attachment if ($this->request->file('attachment')) { $media = $this->getMedia($this->request->file('attachment'), 'invoices'); - $invoice->attachMedia($media, 'attachment'); + $this->invoice->attachMedia($media, 'attachment'); } - $taxes = []; + $this->createItemsAndTotals(); - $tax_total = 0; - $sub_total = 0; - $discount_total = 0; - $discount = $this->request['discount']; + $this->invoice->update($this->request->all()); - if ($this->request['items']) { - foreach ($this->request['items'] as $item) { - $invoice_item = $this->dispatch(new CreateInvoiceItem($item, $invoice, $discount)); + $this->invoice->createRecurring(); - // Calculate totals - $tax_total += $invoice_item->tax; - $sub_total += $invoice_item->total; + event(new InvoiceCreated($this->invoice)); - // Set taxes - if ($invoice_item->item_taxes) { - foreach ($invoice_item->item_taxes as $item_tax) { - if (isset($taxes) && array_key_exists($item_tax['tax_id'], $taxes)) { - $taxes[$item_tax['tax_id']]['amount'] += $item_tax['amount']; - } else { - $taxes[$item_tax['tax_id']] = [ - 'name' => $item_tax['name'], - 'amount' => $item_tax['amount'] - ]; - } - } - } - } - } - - $s_total = $sub_total; - - // Apply discount to total - if ($discount) { - $s_discount = $s_total * ($discount / 100); - $discount_total += $s_discount; - $s_total = $s_total - $s_discount; - } - - $amount = $s_total + $tax_total; - - $this->request['amount'] = money($amount, $this->request['currency_code'])->getAmount(); - - $invoice->update($this->request->all()); - - // Add invoice totals - $this->addTotals($invoice, $this->request, $taxes, $sub_total, $discount_total, $tax_total); - - // Recurring - $invoice->createRecurring(); - - event(new InvoiceCreated($invoice)); - - return $invoice; + return $this->invoice; } - protected function addTotals($invoice, $request, $taxes, $sub_total, $discount_total, $tax_total) + protected function createItemsAndTotals() { - // Check if totals are in request, i.e. api - if (!empty($request['totals'])) { - $sort_order = 1; - - foreach ($request['totals'] as $total) { - $total['invoice_id'] = $invoice->id; - - if (empty($total['sort_order'])) { - $total['sort_order'] = $sort_order; - } - - InvoiceTotal::create($total); - - $sort_order++; - } - - return; - } + // Create items + list($sub_total, $taxes) = $this->createItems(); $sort_order = 1; - // Added invoice sub total + // Add sub total InvoiceTotal::create([ - 'company_id' => $request['company_id'], - 'invoice_id' => $invoice->id, + 'company_id' => $this->invoice->company_id, + 'invoice_id' => $this->invoice->id, 'code' => 'sub_total', 'name' => 'invoices.sub_total', 'amount' => $sub_total, 'sort_order' => $sort_order, ]); + $this->request['amount'] += $sub_total; + $sort_order++; - // Added invoice discount - if ($discount_total) { + // Add discount + if (!empty($this->request['discount'])) { + $discount_total = $sub_total * ($this->request['discount'] / 100); + InvoiceTotal::create([ - 'company_id' => $request['company_id'], - 'invoice_id' => $invoice->id, + 'company_id' => $this->invoice->company_id, + 'invoice_id' => $this->invoice->id, 'code' => 'discount', 'name' => 'invoices.discount', 'amount' => $discount_total, 'sort_order' => $sort_order, ]); - // This is for total - $sub_total = $sub_total - $discount_total; + $this->request['amount'] -= $discount_total; $sort_order++; } - // Added invoice taxes - if (isset($taxes)) { + // Add taxes + if (!empty($taxes)) { foreach ($taxes as $tax) { InvoiceTotal::create([ - 'company_id' => $request['company_id'], - 'invoice_id' => $invoice->id, + 'company_id' => $this->invoice->company_id, + 'invoice_id' => $this->invoice->id, 'code' => 'tax', 'name' => $tax['name'], 'amount' => $tax['amount'], 'sort_order' => $sort_order, ]); + $this->request['amount'] += $tax['amount']; + $sort_order++; } } - // Added invoice total + // Add extra totals, i.e. shipping fee + if (!empty($this->request['totals'])) { + foreach ($this->request['totals'] as $total) { + $total['company_id'] = $this->invoice->company_id; + $total['invoice_id'] = $this->invoice->id; + $total['sort_order'] = $sort_order; + + if (empty($total['code'])) { + $total['code'] = 'extra'; + } + + InvoiceTotal::create($total); + + if (empty($total['operator']) || ($total['operator'] == 'addition')) { + $this->request['amount'] += $total['amount']; + } else { + // subtraction + $this->request['amount'] -= $total['amount']; + } + + $sort_order++; + } + } + + // Add total InvoiceTotal::create([ - 'company_id' => $request['company_id'], - 'invoice_id' => $invoice->id, + 'company_id' => $this->invoice->company_id, + 'invoice_id' => $this->invoice->id, 'code' => 'total', 'name' => 'invoices.total', - 'amount' => $sub_total + $tax_total, + 'amount' => $this->request['amount'], 'sort_order' => $sort_order, ]); } + + protected function createItems() + { + $sub_total = 0; + + $taxes = []; + + if (empty($this->request['items'])) { + return [$sub_total, $taxes]; + } + + foreach ((array) $this->request['items'] as $item) { + if (empty($item['discount'])) { + $item['discount'] = !empty($this->request['discount']) ? !empty($this->request['discount']) : 0; + } + + $invoice_item = $this->dispatch(new CreateInvoiceItem($item, $this->invoice)); + + // Calculate totals + $sub_total += $invoice_item->total; + + if (!$invoice_item->item_taxes) { + continue; + } + + // Set taxes + foreach ((array) $invoice_item->item_taxes as $item_tax) { + if (array_key_exists($item_tax['tax_id'], $taxes)) { + $taxes[$item_tax['tax_id']]['amount'] += $item_tax['amount']; + } else { + $taxes[$item_tax['tax_id']] = [ + 'name' => $item_tax['name'], + 'amount' => $item_tax['amount'] + ]; + } + } + } + + return [$sub_total, $taxes]; + } } diff --git a/app/Jobs/Income/CreateInvoiceItem.php b/app/Jobs/Income/CreateInvoiceItem.php index dc68f5ba9..455e6a9a8 100644 --- a/app/Jobs/Income/CreateInvoiceItem.php +++ b/app/Jobs/Income/CreateInvoiceItem.php @@ -10,24 +10,20 @@ use Illuminate\Support\Str; class CreateInvoiceItem extends Job { - protected $data; + protected $request; protected $invoice; - protected $discount; - /** * Create a new job instance. * - * @param $data + * @param $request * @param $invoice - * @param $discount */ - public function __construct($data, $invoice, $discount = null) + public function __construct($request, $invoice) { - $this->data = $data; + $this->request = $request; $this->invoice = $invoice; - $this->discount = $discount; } /** @@ -37,36 +33,37 @@ class CreateInvoiceItem extends Job */ public function handle() { - $item_id = !empty($this->data['item_id']) ? $this->data['item_id'] : 0; - $item_amount = (double) $this->data['price'] * (double) $this->data['quantity']; + $item_id = !empty($this->request['item_id']) ? $this->request['item_id'] : 0; + $item_amount = (double) $this->request['price'] * (double) $this->request['quantity']; - $item_discount_amount = $item_amount; + $item_discounted_amount = $item_amount; - // Apply discount to tax - if ($this->discount) { - $item_discount_amount = $item_amount - ($item_amount * ($this->discount / 100)); + // Apply discount to amount + if (!empty($this->request['discount'])) { + $item_discounted_amount = $item_amount - ($item_amount * ($this->request['discount'] / 100)); } $tax_amount = 0; $item_taxes = []; $item_tax_total = 0; - if (!empty($this->data['tax_id'])) { - $inclusives = $compounds = $taxes = $fixed_taxes = []; + if (!empty($this->request['tax_id'])) { + $inclusives = $compounds = []; - foreach ((array)$this->data['tax_id'] as $tax_id) { + foreach ((array) $this->request['tax_id'] as $tax_id) { $tax = Tax::find($tax_id); switch ($tax->type) { case 'inclusive': $inclusives[] = $tax; + break; case 'compound': $compounds[] = $tax; + break; case 'fixed': - $fixed_taxes[] = $tax; - $tax_amount = $tax->rate; + $tax_amount = $tax->rate * (double) $this->request['quantity']; $item_taxes[] = [ 'company_id' => $this->invoice->company_id, @@ -77,12 +74,10 @@ class CreateInvoiceItem extends Job ]; $item_tax_total += $tax_amount; + break; - case 'normal': default: - $taxes[] = $tax; - - $tax_amount = ($item_discount_amount / 100) * $tax->rate; + $tax_amount = ($item_discounted_amount / 100) * $tax->rate; $item_taxes[] = [ 'company_id' => $this->invoice->company_id, @@ -93,12 +88,13 @@ class CreateInvoiceItem extends Job ]; $item_tax_total += $tax_amount; + break; } } if ($inclusives) { - $item_amount = $item_discount_amount + $item_tax_total; + $item_amount = $item_discounted_amount + $item_tax_total; $item_base_rate = $item_amount / (1 + collect($inclusives)->sum('rate') / 100); @@ -114,12 +110,12 @@ class CreateInvoiceItem extends Job ]; } - $item_amount = ($item_amount - $item_tax_total) / (1 - $this->discount / 100); + $item_amount = ($item_amount - $item_tax_total) / (1 - $this->request['discount'] / 100); } if ($compounds) { foreach ($compounds as $compound) { - $tax_amount = (($item_discount_amount + $item_tax_total) / 100) * $compound->rate; + $tax_amount = (($item_discounted_amount + $item_tax_total) / 100) * $compound->rate; $item_tax_total += $tax_amount; @@ -138,9 +134,9 @@ class CreateInvoiceItem extends Job 'company_id' => $this->invoice->company_id, 'invoice_id' => $this->invoice->id, 'item_id' => $item_id, - 'name' => Str::limit($this->data['name'], 180, ''), - 'quantity' => (double) $this->data['quantity'], - 'price' => (double) $this->data['price'], + 'name' => Str::limit($this->request['name'], 180, ''), + 'quantity' => (double) $this->request['quantity'], + 'price' => (double) $this->request['price'], 'tax' => $item_tax_total, 'total' => $item_amount, ]); @@ -149,28 +145,15 @@ class CreateInvoiceItem extends Job $invoice_item->inclusives = false; $invoice_item->compounds = false; - // set item_taxes for - if (!empty($this->data['tax_id'])) { + if (!empty($item_taxes)) { $invoice_item->item_taxes = $item_taxes; $invoice_item->inclusives = $inclusives; $invoice_item->compounds = $compounds; - } - if ($item_taxes) { foreach ($item_taxes as $item_tax) { $item_tax['invoice_item_id'] = $invoice_item->id; InvoiceItemTax::create($item_tax); - - // Set taxes - if (isset($taxes) && array_key_exists($item_tax['tax_id'], $taxes)) { - $taxes[$item_tax['tax_id']]['amount'] += $item_tax['amount']; - } else { - $taxes[$item_tax['tax_id']] = [ - 'name' => $item_tax['name'], - 'amount' => $item_tax['amount'] - ]; - } } } diff --git a/app/Jobs/Income/UpdateInvoice.php b/app/Jobs/Income/UpdateInvoice.php index 05579b984..491df644d 100644 --- a/app/Jobs/Income/UpdateInvoice.php +++ b/app/Jobs/Income/UpdateInvoice.php @@ -3,17 +3,17 @@ namespace App\Jobs\Income; use App\Abstracts\Job; -use App\Models\Common\Item; +use App\Events\Income\InvoiceUpdated; +use App\Events\Income\InvoiceUpdating; use App\Models\Income\Invoice; use App\Models\Income\InvoiceTotal; use App\Traits\Currencies; use App\Traits\DateTime; -use App\Traits\Incomes; -use Illuminate\Database\Eloquent\Collection; +use App\Traits\Relationships; class UpdateInvoice extends Job { - use Currencies, DateTime, Incomes; + use Currencies, DateTime, Relationships; protected $invoice; @@ -37,7 +37,11 @@ class UpdateInvoice extends Job */ public function handle() { - event(new \App\Events\Income\InvoiceUpdating($this->invoice, $this->request)); + if (empty($this->request['amount'])) { + $this->request['amount'] = 0; + } + + event(new InvoiceUpdating($this->invoice, $this->request)); // Upload attachment if ($this->request->file('attachment')) { @@ -46,51 +50,7 @@ class UpdateInvoice extends Job $this->invoice->attachMedia($media, 'attachment'); } - $taxes = []; - - $tax_total = 0; - $sub_total = 0; - $discount_total = 0; - $discount = $this->request['discount']; - - if ($this->request['items']) { - $this->deleteRelationships($this->invoice, ['items', 'item_taxes']); - - foreach ($this->request['items'] as $item) { - $invoice_item = dispatch_now(new CreateInvoiceItem($item, $this->invoice, $discount)); - - // Calculate totals - $tax_total += $invoice_item->tax; - $sub_total += $invoice_item->total; - - // Set taxes - if ($invoice_item->item_taxes) { - foreach ($invoice_item->item_taxes as $item_tax) { - if (isset($taxes) && array_key_exists($item_tax['tax_id'], $taxes)) { - $taxes[$item_tax['tax_id']]['amount'] += $item_tax['amount']; - } else { - $taxes[$item_tax['tax_id']] = [ - 'name' => $item_tax['name'], - 'amount' => $item_tax['amount'] - ]; - } - } - } - } - } - - $s_total = $sub_total; - - // Apply discount to total - if ($discount) { - $s_discount = $s_total * ($discount / 100); - $discount_total += $s_discount; - $s_total = $s_total - $s_discount; - } - - $amount = $s_total + $tax_total; - - $this->request['amount'] = money($amount, $this->request['currency_code'])->getAmount(); + $this->createItemsAndTotals(); $invoice_paid = $this->invoice->paid; @@ -100,125 +60,150 @@ class UpdateInvoice extends Job $this->request['invoice_status_code'] = 'partial'; } - $this->invoice->update($this->request->input()); + $this->invoice->update($this->request->all()); - // Delete previous invoice totals - $this->deleteRelationships($this->invoice, 'totals'); - - // Add invoice totals - $this->addTotals($this->invoice, $this->request, $taxes, $sub_total, $discount_total, $tax_total); - - // Recurring $this->invoice->updateRecurring(); - event(new \App\Events\Income\InvoiceUpdated($this->invoice, $this->request)); + event(new InvoiceUpdated($this->invoice, $this->request)); return $this->invoice; } - protected function addTotals($invoice, $request, $taxes, $sub_total, $discount_total, $tax_total) + protected function createItemsAndTotals() { - // Check if totals are in request, i.e. api - if (!empty($request['totals'])) { - $sort_order = 1; + // Create items + list($sub_total, $taxes) = $this->createItems(); - foreach ($request['totals'] as $total) { - $total['invoice_id'] = $invoice->id; - - if (empty($total['sort_order'])) { - $total['sort_order'] = $sort_order; - } - - InvoiceTotal::create($total); - - $sort_order++; - } - - return; - } + // Delete current totals + $this->deleteRelationships($this->invoice, 'totals'); $sort_order = 1; - // Added invoice sub total + // Add sub total InvoiceTotal::create([ - 'company_id' => $request['company_id'], - 'invoice_id' => $invoice->id, + 'company_id' => $this->invoice->company_id, + 'invoice_id' => $this->invoice->id, 'code' => 'sub_total', 'name' => 'invoices.sub_total', 'amount' => $sub_total, 'sort_order' => $sort_order, ]); + $this->request['amount'] += $sub_total; + $sort_order++; - // Added invoice discount - if ($discount_total) { + // Add discount + if (!empty($this->request['discount'])) { + $discount_total = $sub_total * ($this->request['discount'] / 100); + InvoiceTotal::create([ - 'company_id' => $request['company_id'], - 'invoice_id' => $invoice->id, + 'company_id' => $this->invoice->company_id, + 'invoice_id' => $this->invoice->id, 'code' => 'discount', 'name' => 'invoices.discount', 'amount' => $discount_total, 'sort_order' => $sort_order, ]); - // This is for total - $sub_total = $sub_total - $discount_total; + $this->request['amount'] -= $discount_total; $sort_order++; } - // Added invoice taxes - if (isset($taxes)) { + // Add taxes + if (!empty($taxes)) { foreach ($taxes as $tax) { InvoiceTotal::create([ - 'company_id' => $request['company_id'], - 'invoice_id' => $invoice->id, + 'company_id' => $this->invoice->company_id, + 'invoice_id' => $this->invoice->id, 'code' => 'tax', 'name' => $tax['name'], 'amount' => $tax['amount'], 'sort_order' => $sort_order, ]); + $this->request['amount'] += $tax['amount']; + $sort_order++; } } - // Added invoice total + // Add extra totals, i.e. shipping fee + if (!empty($this->request['totals'])) { + foreach ($this->request['totals'] as $total) { + $total['company_id'] = $this->invoice->company_id; + $total['invoice_id'] = $this->invoice->id; + $total['sort_order'] = $sort_order; + + if (empty($total['code'])) { + $total['code'] = 'extra'; + } + + InvoiceTotal::create($total); + + if (empty($total['operator']) || ($total['operator'] == 'addition')) { + $this->request['amount'] += $total['amount']; + } else { + // subtraction + $this->request['amount'] -= $total['amount']; + } + + $sort_order++; + } + } + + // Add total InvoiceTotal::create([ - 'company_id' => $request['company_id'], - 'invoice_id' => $invoice->id, + 'company_id' => $this->invoice->company_id, + 'invoice_id' => $this->invoice->id, 'code' => 'total', 'name' => 'invoices.total', - 'amount' => $sub_total + $tax_total, + 'amount' => $this->request['amount'], 'sort_order' => $sort_order, ]); } - /** - * Mass delete relationships with events being fired. - * - * @param $model - * @param $relationships - * - * @return void - */ - public function deleteRelationships($model, $relationships) + protected function createItems() { - foreach ((array) $relationships as $relationship) { - if (empty($model->$relationship)) { + $sub_total = 0; + + $taxes = []; + + if (empty($this->request['items'])) { + return [$sub_total, $taxes]; + } + + // Delete current items + $this->deleteRelationships($this->invoice, ['items', 'item_taxes']); + + foreach ((array) $this->request['items'] as $item) { + if (empty($item['discount'])) { + $item['discount'] = !empty($this->request['discount']) ? !empty($this->request['discount']) : 0; + } + + $invoice_item = $this->dispatch(new CreateInvoiceItem($item, $this->invoice)); + + // Calculate totals + $sub_total += $invoice_item->total; + + if (!$invoice_item->item_taxes) { continue; } - $items = $model->$relationship->all(); - - if ($items instanceof Collection) { - $items = $items->all(); - } - - foreach ((array) $items as $item) { - $item->delete(); + // Set taxes + foreach ((array) $invoice_item->item_taxes as $item_tax) { + if (array_key_exists($item_tax['tax_id'], $taxes)) { + $taxes[$item_tax['tax_id']]['amount'] += $item_tax['amount']; + } else { + $taxes[$item_tax['tax_id']] = [ + 'name' => $item_tax['name'], + 'amount' => $item_tax['amount'] + ]; + } } } + + return [$sub_total, $taxes]; } }