From 0bbbe25d7fb5d73116379d0dcffd102564c9ad57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cihan=20=C5=9Eent=C3=BCrk?= Date: Mon, 14 Aug 2023 17:21:40 +0300 Subject: [PATCH 1/6] added import and export recurring bills --- app/Exports/Purchases/{ => Bills}/Bills.php | 14 ++-- .../{ => Bills}/Sheets/BillHistories.php | 2 +- .../{ => Bills}/Sheets/BillItemTaxes.php | 2 +- .../{ => Bills}/Sheets/BillItems.php | 2 +- .../{ => Bills}/Sheets/BillTotals.php | 2 +- .../{ => Bills}/Sheets/BillTransactions.php | 2 +- .../Purchases/{ => Bills}/Sheets/Bills.php | 2 +- .../RecurringBills/RecurringBills.php | 36 ++++++++++ .../RecurringBills/Sheets/Recurring.php | 36 ++++++++++ .../Sheets/RecurringBillHistories.php | 37 +++++++++++ .../Sheets/RecurringBillItemTaxes.php | 39 +++++++++++ .../Sheets/RecurringBillItems.php | 44 +++++++++++++ .../Sheets/RecurringBillTotals.php | 38 +++++++++++ .../RecurringBills/Sheets/RecurringBills.php | 65 ++++++++++++++++++ app/Http/Controllers/Purchases/Bills.php | 4 +- .../Controllers/Purchases/RecurringBills.php | 37 +++++++++++ app/Imports/Purchases/{ => Bills}/Bills.php | 14 ++-- .../{ => Bills}/Sheets/BillHistories.php | 2 +- .../{ => Bills}/Sheets/BillItemTaxes.php | 2 +- .../{ => Bills}/Sheets/BillItems.php | 2 +- .../{ => Bills}/Sheets/BillTotals.php | 2 +- .../{ => Bills}/Sheets/BillTransactions.php | 2 +- .../Purchases/{ => Bills}/Sheets/Bills.php | 2 +- .../RecurringBills/RecurringBills.php | 26 ++++++++ .../RecurringBills/Sheets/Recurring.php | 27 ++++++++ .../Sheets/RecurringBillHistories.php | 49 ++++++++++++++ .../Sheets/RecurringBillItemTaxes.php | 66 +++++++++++++++++++ .../Sheets/RecurringBillItems.php | 57 ++++++++++++++++ .../Sheets/RecurringBillTotals.php | 47 +++++++++++++ .../RecurringBills/Sheets/RecurringBills.php | 52 +++++++++++++++ .../purchases/recurring_bills/index.blade.php | 2 +- 31 files changed, 685 insertions(+), 29 deletions(-) rename app/Exports/Purchases/{ => Bills}/Bills.php (62%) rename app/Exports/Purchases/{ => Bills}/Sheets/BillHistories.php (93%) rename app/Exports/Purchases/{ => Bills}/Sheets/BillItemTaxes.php (94%) rename app/Exports/Purchases/{ => Bills}/Sheets/BillItems.php (95%) rename app/Exports/Purchases/{ => Bills}/Sheets/BillTotals.php (93%) rename app/Exports/Purchases/{ => Bills}/Sheets/BillTransactions.php (97%) rename app/Exports/Purchases/{ => Bills}/Sheets/Bills.php (97%) create mode 100644 app/Exports/Purchases/RecurringBills/RecurringBills.php create mode 100644 app/Exports/Purchases/RecurringBills/Sheets/Recurring.php create mode 100644 app/Exports/Purchases/RecurringBills/Sheets/RecurringBillHistories.php create mode 100644 app/Exports/Purchases/RecurringBills/Sheets/RecurringBillItemTaxes.php create mode 100644 app/Exports/Purchases/RecurringBills/Sheets/RecurringBillItems.php create mode 100644 app/Exports/Purchases/RecurringBills/Sheets/RecurringBillTotals.php create mode 100644 app/Exports/Purchases/RecurringBills/Sheets/RecurringBills.php rename app/Imports/Purchases/{ => Bills}/Bills.php (56%) rename app/Imports/Purchases/{ => Bills}/Sheets/BillHistories.php (95%) rename app/Imports/Purchases/{ => Bills}/Sheets/BillItemTaxes.php (97%) rename app/Imports/Purchases/{ => Bills}/Sheets/BillItems.php (96%) rename app/Imports/Purchases/{ => Bills}/Sheets/BillTotals.php (95%) rename app/Imports/Purchases/{ => Bills}/Sheets/BillTransactions.php (95%) rename app/Imports/Purchases/{ => Bills}/Sheets/Bills.php (97%) create mode 100644 app/Imports/Purchases/RecurringBills/RecurringBills.php create mode 100644 app/Imports/Purchases/RecurringBills/Sheets/Recurring.php create mode 100644 app/Imports/Purchases/RecurringBills/Sheets/RecurringBillHistories.php create mode 100644 app/Imports/Purchases/RecurringBills/Sheets/RecurringBillItemTaxes.php create mode 100644 app/Imports/Purchases/RecurringBills/Sheets/RecurringBillItems.php create mode 100644 app/Imports/Purchases/RecurringBills/Sheets/RecurringBillTotals.php create mode 100644 app/Imports/Purchases/RecurringBills/Sheets/RecurringBills.php diff --git a/app/Exports/Purchases/Bills.php b/app/Exports/Purchases/Bills/Bills.php similarity index 62% rename from app/Exports/Purchases/Bills.php rename to app/Exports/Purchases/Bills/Bills.php index 3a5a9115b..dd4213e52 100644 --- a/app/Exports/Purchases/Bills.php +++ b/app/Exports/Purchases/Bills/Bills.php @@ -1,13 +1,13 @@ ids = $ids; + } + + public function sheets(): array + { + return [ + new Recurring($this->ids), + new Base($this->ids), + new RecurringBillItems($this->ids), + new RecurringBillItemTaxes($this->ids), + new RecurringBillHistories($this->ids), + new RecurringBillTotals($this->ids), + ]; + } +} diff --git a/app/Exports/Purchases/RecurringBills/Sheets/Recurring.php b/app/Exports/Purchases/RecurringBills/Sheets/Recurring.php new file mode 100644 index 000000000..20ef7354c --- /dev/null +++ b/app/Exports/Purchases/RecurringBills/Sheets/Recurring.php @@ -0,0 +1,36 @@ +cursor(); + } + + public function map($model): array + { + $model->bill_number = $model->recurable->document_number; + + return parent::map($model); + } + + public function fields(): array + { + return [ + 'recurable_type', + 'bill_number', + 'frequency', + 'interval', + 'started_at', + 'status', + 'limit_by', + 'limit_count', + 'auto_send', + ]; + } +} diff --git a/app/Exports/Purchases/RecurringBills/Sheets/RecurringBillHistories.php b/app/Exports/Purchases/RecurringBills/Sheets/RecurringBillHistories.php new file mode 100644 index 000000000..41af4ea76 --- /dev/null +++ b/app/Exports/Purchases/RecurringBills/Sheets/RecurringBillHistories.php @@ -0,0 +1,37 @@ +billRecurring()->collectForExport($this->ids, null, 'document_id'); + } + + public function map($model): array + { + $document = $model->document; + + if (empty($document)) { + return []; + } + + $model->bill_number = $document->document_number; + + return parent::map($model); + } + + public function fields(): array + { + return [ + 'bill_number', + 'status', + 'notify', + 'description', + ]; + } +} diff --git a/app/Exports/Purchases/RecurringBills/Sheets/RecurringBillItemTaxes.php b/app/Exports/Purchases/RecurringBills/Sheets/RecurringBillItemTaxes.php new file mode 100644 index 000000000..1249a8160 --- /dev/null +++ b/app/Exports/Purchases/RecurringBills/Sheets/RecurringBillItemTaxes.php @@ -0,0 +1,39 @@ +billRecurring()->collectForExport($this->ids, null, 'document_id'); + } + + public function map($model): array + { + $document = $model->document; + + if (empty($document)) { + return []; + } + + $model->bill_number = $document->document_number; + $model->item_name = $model->item->name; + $model->tax_rate = $model->tax->rate; + + return parent::map($model); + } + + public function fields(): array + { + return [ + 'bill_number', + 'item_name', + 'tax_rate', + 'amount', + ]; + } +} diff --git a/app/Exports/Purchases/RecurringBills/Sheets/RecurringBillItems.php b/app/Exports/Purchases/RecurringBills/Sheets/RecurringBillItems.php new file mode 100644 index 000000000..1e7eb345e --- /dev/null +++ b/app/Exports/Purchases/RecurringBills/Sheets/RecurringBillItems.php @@ -0,0 +1,44 @@ +billRecurring()->collectForExport($this->ids, null, 'document_id'); + } + + public function map($model): array + { + $document = $model->document; + + if (empty($document)) { + return []; + } + + $model->bill_number = $document->document_number; + $model->item_name = $model->item->name; + $model->item_description = $model->item->description; + $model->item_type = $model->item->type; + + return parent::map($model); + } + + public function fields(): array + { + return [ + 'bill_number', + 'item_name', + 'item_description', + 'item_type', + 'quantity', + 'price', + 'total', + 'tax', + ]; + } +} diff --git a/app/Exports/Purchases/RecurringBills/Sheets/RecurringBillTotals.php b/app/Exports/Purchases/RecurringBills/Sheets/RecurringBillTotals.php new file mode 100644 index 000000000..fdaadafc6 --- /dev/null +++ b/app/Exports/Purchases/RecurringBills/Sheets/RecurringBillTotals.php @@ -0,0 +1,38 @@ +billRecurring()->collectForExport($this->ids, null, 'document_id'); + } + + public function map($model): array + { + $document = $model->document; + + if (empty($document)) { + return []; + } + + $model->bill_number = $document->document_number; + + return parent::map($model); + } + + public function fields(): array + { + return [ + 'bill_number', + 'code', + 'name', + 'amount', + 'sort_order', + ]; + } +} diff --git a/app/Exports/Purchases/RecurringBills/Sheets/RecurringBills.php b/app/Exports/Purchases/RecurringBills/Sheets/RecurringBills.php new file mode 100644 index 000000000..beda1e22c --- /dev/null +++ b/app/Exports/Purchases/RecurringBills/Sheets/RecurringBills.php @@ -0,0 +1,65 @@ +billRecurring()->collectForExport($this->ids, ['document_number' => 'desc']); + } + + public function map($model): array + { + $country = null; + + if ($model->contact_country && array_key_exists($model->contact_country, trans('countries'))) { + $country = trans('countries.' . $model->contact_country); + } + + $model->category_name = $model->category->name; + $model->bill_number = $model->document_number; + $model->billed_at = $model->issued_at; + $model->contact_country = $country; + + return parent::map($model); + } + + public function fields(): array + { + return [ + 'bill_number', + 'order_number', + 'status', + 'billed_at', + 'due_at', + 'amount', + 'currency_code', + 'currency_rate', + 'category_name', + 'contact_name', + 'contact_email', + 'contact_tax_number', + 'contact_phone', + 'contact_address', + 'contact_country', + 'contact_state', + 'contact_zip_code', + 'contact_city', + 'notes', + ]; + } + + public function columnFormats(): array + { + return [ + 'D' => NumberFormat::FORMAT_DATE_YYYYMMDD, + 'E' => NumberFormat::FORMAT_DATE_YYYYMMDD, + ]; + } +} diff --git a/app/Http/Controllers/Purchases/Bills.php b/app/Http/Controllers/Purchases/Bills.php index 5465fd625..69b615cfc 100644 --- a/app/Http/Controllers/Purchases/Bills.php +++ b/app/Http/Controllers/Purchases/Bills.php @@ -3,10 +3,10 @@ namespace App\Http\Controllers\Purchases; use App\Abstracts\Http\Controller; -use App\Exports\Purchases\Bills as Export; +use App\Exports\Purchases\Bills\Bills as Export; use App\Http\Requests\Common\Import as ImportRequest; use App\Http\Requests\Document\Document as Request; -use App\Imports\Purchases\Bills as Import; +use App\Imports\Purchases\Bills\Bills as Import; use App\Jobs\Document\CreateDocument; use App\Jobs\Document\DeleteDocument; use App\Jobs\Document\DuplicateDocument; diff --git a/app/Http/Controllers/Purchases/RecurringBills.php b/app/Http/Controllers/Purchases/RecurringBills.php index 4fb9f874f..ffa34b579 100644 --- a/app/Http/Controllers/Purchases/RecurringBills.php +++ b/app/Http/Controllers/Purchases/RecurringBills.php @@ -3,7 +3,10 @@ namespace App\Http\Controllers\Purchases; use App\Abstracts\Http\Controller; +use App\Exports\Purchases\RecurringBills\RecurringBills as Export; +use App\Http\Requests\Common\Import as ImportRequest; use App\Http\Requests\Document\Document as Request; +use App\Imports\Purchases\RecurringBills\RecurringBills as Import; use App\Jobs\Document\CreateDocument; use App\Jobs\Document\DuplicateDocument; use App\Jobs\Document\UpdateDocument; @@ -114,6 +117,30 @@ class RecurringBills extends Controller return redirect()->route('recurring-bills.edit', $clone->id); } + /** + * Import the specified resource. + * + * @param ImportRequest $request + * + * @return Response + */ + public function import(ImportRequest $request) + { + $response = $this->importExcel(new Import, $request, trans_choice('general.recurring_bills', 2)); + + if ($response['success']) { + $response['redirect'] = route('recurring-bills.index'); + + flash($response['message'])->success(); + } else { + $response['redirect'] = route('import.create', ['purchases', 'recurring-bills']); + + flash($response['message'])->error()->important(); + } + + return response()->json($response); + } + /** * Show the form for editing the specified resource. * @@ -155,6 +182,16 @@ class RecurringBills extends Controller return response()->json($response); } + /** + * Export the specified resource. + * + * @return Response + */ + public function export() + { + return $this->exportExcel(new Export, trans_choice('general.recurring_bills', 2)); + } + /** * End recurring template. * diff --git a/app/Imports/Purchases/Bills.php b/app/Imports/Purchases/Bills/Bills.php similarity index 56% rename from app/Imports/Purchases/Bills.php rename to app/Imports/Purchases/Bills/Bills.php index 18b383af2..541d98899 100644 --- a/app/Imports/Purchases/Bills.php +++ b/app/Imports/Purchases/Bills/Bills.php @@ -1,14 +1,14 @@ new Base(), + 'recurring_bill_items' => new RecurringBillItems(), + 'recurring_bill_item_taxes' => new RecurringBillItemTaxes(), + 'recurring_bill_histories' => new RecurringBillHistories(), + 'recurring_bill_totals' => new RecurringBillTotals(), + 'recurring' => new Recurring(), + ]; + } +} diff --git a/app/Imports/Purchases/RecurringBills/Sheets/Recurring.php b/app/Imports/Purchases/RecurringBills/Sheets/Recurring.php new file mode 100644 index 000000000..da034faa0 --- /dev/null +++ b/app/Imports/Purchases/RecurringBills/Sheets/Recurring.php @@ -0,0 +1,27 @@ +number($row['bill_number']) + ->pluck('id') + ->first(); + + return $row; + } +} diff --git a/app/Imports/Purchases/RecurringBills/Sheets/RecurringBillHistories.php b/app/Imports/Purchases/RecurringBills/Sheets/RecurringBillHistories.php new file mode 100644 index 000000000..fafa4a14a --- /dev/null +++ b/app/Imports/Purchases/RecurringBills/Sheets/RecurringBillHistories.php @@ -0,0 +1,49 @@ +isEmpty($row, 'bill_number')) { + return []; + } + + $row['bill_number'] = (string) $row['bill_number']; + + $row = parent::map($row); + + $row['document_id'] = (int) Document::where('type', '=', Document::BILL_RECURRING_TYPE) + ->number($row['bill_number']) + ->pluck('id') + ->first(); + + $row['notify'] = (int) $row['notify']; + + $row['type'] = Document::BILL_RECURRING_TYPE; + + return $row; + } + + public function prepareRules(array $rules): array + { + $rules['bill_number'] = 'required|string'; + + unset($rules['bill_id']); + + return $rules; + } +} diff --git a/app/Imports/Purchases/RecurringBills/Sheets/RecurringBillItemTaxes.php b/app/Imports/Purchases/RecurringBills/Sheets/RecurringBillItemTaxes.php new file mode 100644 index 000000000..daf80a71c --- /dev/null +++ b/app/Imports/Purchases/RecurringBills/Sheets/RecurringBillItemTaxes.php @@ -0,0 +1,66 @@ +isEmpty($row, 'bill_number')) { + return []; + } + + $row['bill_number'] = (string) $row['bill_number']; + + $row = parent::map($row); + + $row['document_id'] = (int) Document::where('type', '=', Document::BILL_RECURRING_TYPE) + ->number($row['bill_number']) + ->pluck('id') + ->first(); + + if (empty($row['document_item_id']) && !empty($row['item_name'])) { + $item_id = Item::name($row['item_name'])->pluck('id')->first(); + + $row['document_item_id'] = DocumentItem::where('type', '=', Document::BILL_RECURRING_TYPE) + ->where('item_id', $item_id) + ->pluck('id') + ->first(); + } + + $row['tax_id'] = $this->getTaxId($row); + + if (empty($row['name']) && !empty($row['item_name'])) { + $row['name'] = $row['item_name']; + } + + $row['amount'] = (double) $row['amount']; + + $row['type'] = Document::BILL_RECURRING_TYPE; + + return $row; + } + + public function prepareRules(array $rules): array + { + $rules['bill_number'] = 'required|string'; + + unset($rules['bill_id']); + + return $rules; + } +} diff --git a/app/Imports/Purchases/RecurringBills/Sheets/RecurringBillItems.php b/app/Imports/Purchases/RecurringBills/Sheets/RecurringBillItems.php new file mode 100644 index 000000000..9a4ac6ca3 --- /dev/null +++ b/app/Imports/Purchases/RecurringBills/Sheets/RecurringBillItems.php @@ -0,0 +1,57 @@ +isEmpty($row, 'bill_number')) { + return []; + } + + $row['bill_number'] = (string) $row['bill_number']; + + $row = parent::map($row); + + $row['document_id'] = (int) Document::where('type', '=', Document::BILL_RECURRING_TYPE) + ->number($row['bill_number']) + ->pluck('id') + ->first(); + + if (empty($row['item_id']) && !empty($row['item_name'])) { + $row['item_id'] = $this->getItemIdFromName($row); + + $row['name'] = $row['item_name']; + } + + $row['description'] = !empty($row['item_description']) ? $row['item_description'] : ''; + + $row['tax'] = (double) $row['tax']; + $row['tax_id'] = 0; + $row['type'] = Document::BILL_RECURRING_TYPE; + + return $row; + } + + public function prepareRules(array $rules): array + { + $rules['bill_number'] = 'required|string'; + + unset($rules['bill_id']); + + return $rules; + } +} diff --git a/app/Imports/Purchases/RecurringBills/Sheets/RecurringBillTotals.php b/app/Imports/Purchases/RecurringBills/Sheets/RecurringBillTotals.php new file mode 100644 index 000000000..d0038cef9 --- /dev/null +++ b/app/Imports/Purchases/RecurringBills/Sheets/RecurringBillTotals.php @@ -0,0 +1,47 @@ +isEmpty($row, 'bill_number')) { + return []; + } + + $row['bill_number'] = (string) $row['bill_number']; + + $row = parent::map($row); + + $row['document_id'] = (int) Document::where('type', '=', Document::BILL_RECURRING_TYPE) + ->number($row['bill_number']) + ->pluck('id') + ->first(); + + $row['type'] = Document::BILL_RECURRING_TYPE; + + return $row; + } + + public function prepareRules(array $rules): array + { + $rules['bill_number'] = 'required|string'; + + unset($rules['bill_id']); + + return $rules; + } +} diff --git a/app/Imports/Purchases/RecurringBills/Sheets/RecurringBills.php b/app/Imports/Purchases/RecurringBills/Sheets/RecurringBills.php new file mode 100644 index 000000000..f52582643 --- /dev/null +++ b/app/Imports/Purchases/RecurringBills/Sheets/RecurringBills.php @@ -0,0 +1,52 @@ +isEmpty($row, 'bill_number')) { + return []; + } + + $row['bill_number'] = (string) $row['bill_number']; + + $row = parent::map($row); + + $country = array_search($row['contact_country'], trans('countries')); + + $row['document_number'] = $row['bill_number']; + $row['issued_at'] = $row['billed_at']; + $row['category_id'] = $this->getCategoryId($row, 'expense'); + $row['contact_id'] = $this->getContactId($row, 'vendor'); + $row['currency_code'] = $this->getCurrencyCode($row); + $row['type'] = Model::BILL_RECURRING_TYPE; + $row['contact_country'] = !empty($country) ? $country : null; + + return $row; + } + + public function prepareRules(array $rules): array + { + $rules['bill_number'] = Str::replaceFirst('unique:documents,NULL', 'unique:documents,document_number', $rules['document_number']); + $rules['billed_at'] = $rules['issued_at']; + $rules['currency_rate'] = 'required|gt:0'; + + unset($rules['document_number'], $rules['issued_at'], $rules['type']); + + return $rules; + } +} diff --git a/resources/views/purchases/recurring_bills/index.blade.php b/resources/views/purchases/recurring_bills/index.blade.php index 262790cb1..6127bbb1f 100644 --- a/resources/views/purchases/recurring_bills/index.blade.php +++ b/resources/views/purchases/recurring_bills/index.blade.php @@ -14,7 +14,7 @@ - + From bd15a1cd0ee846607d4ff6ba9cd6b1206a0b524a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cihan=20=C5=9Eent=C3=BCrk?= Date: Mon, 14 Aug 2023 17:23:01 +0300 Subject: [PATCH 2/6] added import and export recurring invoices --- app/Exports/Sales/{ => Invoices}/Invoices.php | 14 ++-- .../Sheets/InvoiceHistories.php | 2 +- .../Sheets/InvoiceItemTaxes.php | 2 +- .../{ => Invoices}/Sheets/InvoiceItems.php | 2 +- .../{ => Invoices}/Sheets/InvoiceTotals.php | 2 +- .../Sheets/InvoiceTransactions.php | 2 +- .../Sales/{ => Invoices}/Sheets/Invoices.php | 2 +- .../RecurringInvoices/RecurringInvoices.php | 36 ++++++++++ .../RecurringInvoices/Sheets/Recurring.php | 36 ++++++++++ .../Sheets/RecurringInvoiceHistories.php | 37 ++++++++++ .../Sheets/RecurringInvoiceItemTaxes.php | 39 +++++++++++ .../Sheets/RecurringInvoiceItems.php | 44 ++++++++++++ .../Sheets/RecurringInvoiceTotals.php | 38 +++++++++++ .../Sheets/RecurringInvoices.php | 67 +++++++++++++++++++ app/Http/Controllers/Sales/Invoices.php | 4 +- .../Controllers/Sales/RecurringInvoices.php | 37 ++++++++++ app/Imports/Sales/{ => Invoices}/Invoices.php | 14 ++-- .../Sheets/InvoiceHistories.php | 2 +- .../Sheets/InvoiceItemTaxes.php | 2 +- .../{ => Invoices}/Sheets/InvoiceItems.php | 2 +- .../{ => Invoices}/Sheets/InvoiceTotals.php | 2 +- .../Sheets/InvoiceTransactions.php | 2 +- .../Sales/{ => Invoices}/Sheets/Invoices.php | 2 +- .../RecurringInvoices/RecurringInvoices.php | 26 +++++++ .../RecurringInvoices/Sheets/Recurring.php | 27 ++++++++ .../Sheets/RecurringInvoiceHistories.php | 49 ++++++++++++++ .../Sheets/RecurringInvoiceItemTaxes.php | 66 ++++++++++++++++++ .../Sheets/RecurringInvoiceItems.php | 57 ++++++++++++++++ .../Sheets/RecurringInvoiceTotals.php | 47 +++++++++++++ .../Sheets/RecurringInvoices.php | 52 ++++++++++++++ .../sales/recurring_invoices/index.blade.php | 2 +- 31 files changed, 687 insertions(+), 29 deletions(-) rename app/Exports/Sales/{ => Invoices}/Invoices.php (62%) rename app/Exports/Sales/{ => Invoices}/Sheets/InvoiceHistories.php (94%) rename app/Exports/Sales/{ => Invoices}/Sheets/InvoiceItemTaxes.php (94%) rename app/Exports/Sales/{ => Invoices}/Sheets/InvoiceItems.php (95%) rename app/Exports/Sales/{ => Invoices}/Sheets/InvoiceTotals.php (94%) rename app/Exports/Sales/{ => Invoices}/Sheets/InvoiceTransactions.php (97%) rename app/Exports/Sales/{ => Invoices}/Sheets/Invoices.php (97%) create mode 100644 app/Exports/Sales/RecurringInvoices/RecurringInvoices.php create mode 100644 app/Exports/Sales/RecurringInvoices/Sheets/Recurring.php create mode 100644 app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoiceHistories.php create mode 100644 app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoiceItemTaxes.php create mode 100644 app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoiceItems.php create mode 100644 app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoiceTotals.php create mode 100644 app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoices.php rename app/Imports/Sales/{ => Invoices}/Invoices.php (57%) rename app/Imports/Sales/{ => Invoices}/Sheets/InvoiceHistories.php (95%) rename app/Imports/Sales/{ => Invoices}/Sheets/InvoiceItemTaxes.php (97%) rename app/Imports/Sales/{ => Invoices}/Sheets/InvoiceItems.php (96%) rename app/Imports/Sales/{ => Invoices}/Sheets/InvoiceTotals.php (95%) rename app/Imports/Sales/{ => Invoices}/Sheets/InvoiceTransactions.php (96%) rename app/Imports/Sales/{ => Invoices}/Sheets/Invoices.php (97%) create mode 100644 app/Imports/Sales/RecurringInvoices/RecurringInvoices.php create mode 100644 app/Imports/Sales/RecurringInvoices/Sheets/Recurring.php create mode 100644 app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoiceHistories.php create mode 100644 app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoiceItemTaxes.php create mode 100644 app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoiceItems.php create mode 100644 app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoiceTotals.php create mode 100644 app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoices.php diff --git a/app/Exports/Sales/Invoices.php b/app/Exports/Sales/Invoices/Invoices.php similarity index 62% rename from app/Exports/Sales/Invoices.php rename to app/Exports/Sales/Invoices/Invoices.php index 3c2148d96..d24ba473e 100644 --- a/app/Exports/Sales/Invoices.php +++ b/app/Exports/Sales/Invoices/Invoices.php @@ -1,13 +1,13 @@ ids = $ids; + } + + public function sheets(): array + { + return [ + new Recurring($this->ids), + new Base($this->ids), + new RecurringInvoiceItems($this->ids), + new RecurringInvoiceItemTaxes($this->ids), + new RecurringInvoiceHistories($this->ids), + new RecurringInvoiceTotals($this->ids), + ]; + } +} diff --git a/app/Exports/Sales/RecurringInvoices/Sheets/Recurring.php b/app/Exports/Sales/RecurringInvoices/Sheets/Recurring.php new file mode 100644 index 000000000..08048a4fa --- /dev/null +++ b/app/Exports/Sales/RecurringInvoices/Sheets/Recurring.php @@ -0,0 +1,36 @@ +cursor(); + } + + public function map($model): array + { + $model->invoice_number = $model->recurable->document_number; + + return parent::map($model); + } + + public function fields(): array + { + return [ + 'recurable_type', + 'invoice_number', + 'frequency', + 'interval', + 'started_at', + 'status', + 'limit_by', + 'limit_count', + 'auto_send', + ]; + } +} diff --git a/app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoiceHistories.php b/app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoiceHistories.php new file mode 100644 index 000000000..df0dd0727 --- /dev/null +++ b/app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoiceHistories.php @@ -0,0 +1,37 @@ +invoiceRecurring()->collectForExport($this->ids, null, 'document_id'); + } + + public function map($model): array + { + $document = $model->document; + + if (empty($document)) { + return []; + } + + $model->invoice_number = $document->document_number; + + return parent::map($model); + } + + public function fields(): array + { + return [ + 'invoice_number', + 'status', + 'notify', + 'description', + ]; + } +} diff --git a/app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoiceItemTaxes.php b/app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoiceItemTaxes.php new file mode 100644 index 000000000..fcf783945 --- /dev/null +++ b/app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoiceItemTaxes.php @@ -0,0 +1,39 @@ +invoiceRecurring()->collectForExport($this->ids, null, 'document_id'); + } + + public function map($model): array + { + $document = $model->document; + + if (empty($document)) { + return []; + } + + $model->invoice_number = $document->document_number; + $model->item_name = $model->item->name; + $model->tax_rate = $model->tax->rate; + + return parent::map($model); + } + + public function fields(): array + { + return [ + 'invoice_number', + 'item_name', + 'tax_rate', + 'amount', + ]; + } +} diff --git a/app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoiceItems.php b/app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoiceItems.php new file mode 100644 index 000000000..fabc31c80 --- /dev/null +++ b/app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoiceItems.php @@ -0,0 +1,44 @@ +invoiceRecurring()->collectForExport($this->ids, null, 'document_id'); + } + + public function map($model): array + { + $document = $model->document; + + if (empty($document)) { + return []; + } + + $model->invoice_number = $document->document_number; + $model->item_name = $model->item->name; + $model->item_description = $model->item->description; + $model->item_type = $model->item->type; + + return parent::map($model); + } + + public function fields(): array + { + return [ + 'invoice_number', + 'item_name', + 'item_description', + 'item_type', + 'quantity', + 'price', + 'total', + 'tax', + ]; + } +} diff --git a/app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoiceTotals.php b/app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoiceTotals.php new file mode 100644 index 000000000..8d7b7af4f --- /dev/null +++ b/app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoiceTotals.php @@ -0,0 +1,38 @@ +invoiceRecurring()->collectForExport($this->ids, null, 'document_id'); + } + + public function map($model): array + { + $document = $model->document; + + if (empty($document)) { + return []; + } + + $model->invoice_number = $document->document_number; + + return parent::map($model); + } + + public function fields(): array + { + return [ + 'invoice_number', + 'code', + 'name', + 'amount', + 'sort_order', + ]; + } +} diff --git a/app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoices.php b/app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoices.php new file mode 100644 index 000000000..923ee94e3 --- /dev/null +++ b/app/Exports/Sales/RecurringInvoices/Sheets/RecurringInvoices.php @@ -0,0 +1,67 @@ +invoiceRecurring()->collectForExport($this->ids, ['document_number' => 'desc']); + } + + public function map($model): array + { + $country = null; + + if ($model->contact_country && array_key_exists($model->contact_country, trans('countries'))) { + $country = trans('countries.' . $model->contact_country); + } + + $model->category_name = $model->category->name; + $model->invoice_number = $model->document_number; + $model->invoiced_at = $model->issued_at; + $model->contact_country = $country; + + + return parent::map($model); + } + + public function fields(): array + { + return [ + 'invoice_number', + 'order_number', + 'status', + 'invoiced_at', + 'due_at', + 'amount', + 'currency_code', + 'currency_rate', + 'category_name', + 'contact_name', + 'contact_email', + 'contact_tax_number', + 'contact_phone', + 'contact_address', + 'contact_country', + 'contact_state', + 'contact_zip_code', + 'contact_city', + 'notes', + 'footer', + ]; + } + + public function columnFormats(): array + { + return [ + 'D' => NumberFormat::FORMAT_DATE_YYYYMMDD, + 'E' => NumberFormat::FORMAT_DATE_YYYYMMDD, + ]; + } +} diff --git a/app/Http/Controllers/Sales/Invoices.php b/app/Http/Controllers/Sales/Invoices.php index 3730c5d18..4dc622d51 100644 --- a/app/Http/Controllers/Sales/Invoices.php +++ b/app/Http/Controllers/Sales/Invoices.php @@ -3,10 +3,10 @@ namespace App\Http\Controllers\Sales; use App\Abstracts\Http\Controller; -use App\Exports\Sales\Invoices as Export; +use App\Exports\Sales\Invoices\Invoices as Export; use App\Http\Requests\Common\Import as ImportRequest; use App\Http\Requests\Document\Document as Request; -use App\Imports\Sales\Invoices as Import; +use App\Imports\Sales\Invoices\Invoices as Import; use App\Jobs\Document\CreateDocument; use App\Jobs\Document\DeleteDocument; use App\Jobs\Document\DuplicateDocument; diff --git a/app/Http/Controllers/Sales/RecurringInvoices.php b/app/Http/Controllers/Sales/RecurringInvoices.php index 265e036f6..e1a0bec87 100644 --- a/app/Http/Controllers/Sales/RecurringInvoices.php +++ b/app/Http/Controllers/Sales/RecurringInvoices.php @@ -3,7 +3,10 @@ namespace App\Http\Controllers\Sales; use App\Abstracts\Http\Controller; +use App\Exports\Sales\RecurringInvoices\RecurringInvoices as Export; +use App\Http\Requests\Common\Import as ImportRequest; use App\Http\Requests\Document\Document as Request; +use App\Imports\Sales\RecurringInvoices\RecurringInvoices as Import; use App\Jobs\Document\CreateDocument; use App\Jobs\Document\DuplicateDocument; use App\Jobs\Document\UpdateDocument; @@ -114,6 +117,30 @@ class RecurringInvoices extends Controller return redirect()->route('recurring-invoices.edit', $clone->id); } + /** + * Import the specified resource. + * + * @param ImportRequest $request + * + * @return Response + */ + public function import(ImportRequest $request) + { + $response = $this->importExcel(new Import, $request, trans_choice('general.recurring_invoices', 2)); + + if ($response['success']) { + $response['redirect'] = route('recurring-invoices.index'); + + flash($response['message'])->success(); + } else { + $response['redirect'] = route('import.create', ['sales', 'recurring-invoices']); + + flash($response['message'])->error()->important(); + } + + return response()->json($response); + } + /** * Show the form for editing the specified resource. * @@ -155,6 +182,16 @@ class RecurringInvoices extends Controller return response()->json($response); } + /** + * Export the specified resource. + * + * @return Response + */ + public function export() + { + return $this->exportExcel(new Export, trans_choice('general.recurring_invoices', 2)); + } + /** * End recurring template. * diff --git a/app/Imports/Sales/Invoices.php b/app/Imports/Sales/Invoices/Invoices.php similarity index 57% rename from app/Imports/Sales/Invoices.php rename to app/Imports/Sales/Invoices/Invoices.php index 278091650..7dfa1b274 100644 --- a/app/Imports/Sales/Invoices.php +++ b/app/Imports/Sales/Invoices/Invoices.php @@ -1,14 +1,14 @@ new Base(), + 'recurring_invoice_items' => new RecurringInvoiceItems(), + 'recurring_invoice_item_taxes' => new RecurringInvoiceItemTaxes(), + 'recurring_invoice_histories' => new RecurringInvoiceHistories(), + 'recurring_invoice_totals' => new RecurringInvoiceTotals(), + 'recurring' => new Recurring(), + ]; + } +} diff --git a/app/Imports/Sales/RecurringInvoices/Sheets/Recurring.php b/app/Imports/Sales/RecurringInvoices/Sheets/Recurring.php new file mode 100644 index 000000000..bf71f3ef9 --- /dev/null +++ b/app/Imports/Sales/RecurringInvoices/Sheets/Recurring.php @@ -0,0 +1,27 @@ +number($row['invoice_number']) + ->pluck('id') + ->first(); + + return $row; + } +} diff --git a/app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoiceHistories.php b/app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoiceHistories.php new file mode 100644 index 000000000..f04c36c64 --- /dev/null +++ b/app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoiceHistories.php @@ -0,0 +1,49 @@ +isEmpty($row, 'invoice_number')) { + return []; + } + + $row['invoice_number'] = (string) $row['invoice_number']; + + $row = parent::map($row); + + $row['document_id'] = (int) Document::where('type', '=', Document::INVOICE_RECURRING_TYPE) + ->number($row['invoice_number']) + ->pluck('id') + ->first(); + + $row['notify'] = (int) $row['notify']; + + $row['type'] = Document::INVOICE_RECURRING_TYPE; + + return $row; + } + + public function prepareRules(array $rules): array + { + $rules['invoice_number'] = 'required|string'; + + unset($rules['invoice_id']); + + return $rules; + } +} diff --git a/app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoiceItemTaxes.php b/app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoiceItemTaxes.php new file mode 100644 index 000000000..8b7c633d3 --- /dev/null +++ b/app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoiceItemTaxes.php @@ -0,0 +1,66 @@ +isEmpty($row, 'invoice_number')) { + return []; + } + + $row['invoice_number'] = (string) $row['invoice_number']; + + $row = parent::map($row); + + $row['document_id'] = (int) Document::where('type', '=', Document::INVOICE_RECURRING_TYPE) + ->number($row['invoice_number']) + ->pluck('id') + ->first(); + + if (empty($row['document_item_id']) && !empty($row['item_name'])) { + $item_id = Item::name($row['item_name'])->pluck('id')->first(); + + $row['document_item_id'] = DocumentItem::where('type', '=', Document::INVOICE_RECURRING_TYPE) + ->where('item_id', $item_id) + ->pluck('id') + ->first(); + } + + $row['tax_id'] = $this->getTaxId($row); + + if (empty($row['name']) && !empty($row['item_name'])) { + $row['name'] = $row['item_name']; + } + + $row['amount'] = (double) $row['amount']; + + $row['type'] = Document::INVOICE_RECURRING_TYPE; + + return $row; + } + + public function prepareRules(array $rules): array + { + $rules['invoice_number'] = 'required|string'; + + unset($rules['invoice_id']); + + return $rules; + } +} diff --git a/app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoiceItems.php b/app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoiceItems.php new file mode 100644 index 000000000..5f134e399 --- /dev/null +++ b/app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoiceItems.php @@ -0,0 +1,57 @@ +isEmpty($row, 'invoice_number')) { + return []; + } + + $row['invoice_number'] = (string) $row['invoice_number']; + + $row = parent::map($row); + + $row['document_id'] = (int) Document::where('type', '=', Document::INVOICE_RECURRING_TYPE) + ->number($row['invoice_number']) + ->pluck('id') + ->first(); + + if (empty($row['item_id']) && ! empty($row['item_name'])) { + $row['item_id'] = $this->getItemIdFromName($row); + + $row['name'] = $row['item_name']; + } + + $row['description'] = !empty($row['item_description']) ? $row['item_description'] : ''; + + $row['tax'] = (double) $row['tax']; + $row['tax_id'] = 0; + $row['type'] = Document::INVOICE_RECURRING_TYPE; + + return $row; + } + + public function prepareRules(array $rules): array + { + $rules['invoice_number'] = 'required|string'; + + unset($rules['invoice_id']); + + return $rules; + } +} diff --git a/app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoiceTotals.php b/app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoiceTotals.php new file mode 100644 index 000000000..1dd454207 --- /dev/null +++ b/app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoiceTotals.php @@ -0,0 +1,47 @@ +isEmpty($row, 'invoice_number')) { + return []; + } + + $row['invoice_number'] = (string) $row['invoice_number']; + + $row = parent::map($row); + + $row['document_id'] = (int) Document::where('type', '=', Document::INVOICE_RECURRING_TYPE) + ->number($row['invoice_number']) + ->pluck('id') + ->first(); + + $row['type'] = Document::INVOICE_RECURRING_TYPE; + + return $row; + } + + public function prepareRules(array $rules): array + { + $rules['invoice_number'] = 'required|string'; + + unset($rules['invoice_id']); + + return $rules; + } +} diff --git a/app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoices.php b/app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoices.php new file mode 100644 index 000000000..fb4dd810d --- /dev/null +++ b/app/Imports/Sales/RecurringInvoices/Sheets/RecurringInvoices.php @@ -0,0 +1,52 @@ +isEmpty($row, 'invoice_number')) { + return []; + } + + $row['invoice_number'] = (string) $row['invoice_number']; + + $row = parent::map($row); + + $country = array_search($row['contact_country'], trans('countries')); + + $row['document_number'] = $row['invoice_number']; + $row['issued_at'] = $row['invoiced_at']; + $row['category_id'] = $this->getCategoryId($row, 'income'); + $row['contact_id'] = $this->getContactId($row, 'customer'); + $row['currency_code'] = $this->getCurrencyCode($row); + $row['type'] = Model::INVOICE_RECURRING_TYPE; + $row['contact_country'] = !empty($country) ? $country : null; + + return $row; + } + + public function prepareRules(array $rules): array + { + $rules['invoice_number'] = Str::replaceFirst('unique:documents,NULL', 'unique:documents,document_number', $rules['document_number']); + $rules['invoiced_at'] = $rules['issued_at']; + $rules['currency_rate'] = 'required|gt:0'; + + unset($rules['document_number'], $rules['issued_at'], $rules['type']); + + return $rules; + } +} diff --git a/resources/views/sales/recurring_invoices/index.blade.php b/resources/views/sales/recurring_invoices/index.blade.php index b604be522..c978a1151 100644 --- a/resources/views/sales/recurring_invoices/index.blade.php +++ b/resources/views/sales/recurring_invoices/index.blade.php @@ -14,7 +14,7 @@ - + From f2ec9ffe0c8f55efc92034499e6245a85c8b6be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cihan=20=C5=9Eent=C3=BCrk?= Date: Mon, 14 Aug 2023 17:23:36 +0300 Subject: [PATCH 3/6] added import and export recurring transactions --- app/Exports/Banking/RecurringTransactions.php | 28 ++++++++++ app/Exports/Banking/Sheets/Recurring.php | 36 +++++++++++++ .../Banking/Sheets/RecurringTransactions.php | 53 +++++++++++++++++++ .../Banking/RecurringTransactions.php | 43 +++++++++++++-- app/Imports/Banking/RecurringTransactions.php | 18 +++++++ app/Imports/Banking/Sheets/Recurring.php | 24 +++++++++ .../Banking/Sheets/RecurringTransactions.php | 38 +++++++++++++ .../recurring_transactions/index.blade.php | 46 ++++++++++++---- 8 files changed, 272 insertions(+), 14 deletions(-) create mode 100644 app/Exports/Banking/RecurringTransactions.php create mode 100644 app/Exports/Banking/Sheets/Recurring.php create mode 100644 app/Exports/Banking/Sheets/RecurringTransactions.php create mode 100644 app/Imports/Banking/RecurringTransactions.php create mode 100644 app/Imports/Banking/Sheets/Recurring.php create mode 100644 app/Imports/Banking/Sheets/RecurringTransactions.php diff --git a/app/Exports/Banking/RecurringTransactions.php b/app/Exports/Banking/RecurringTransactions.php new file mode 100644 index 000000000..87d51745c --- /dev/null +++ b/app/Exports/Banking/RecurringTransactions.php @@ -0,0 +1,28 @@ +ids = $ids; + } + + public function sheets(): array + { + return [ + new Recurring($this->ids), + new Base($this->ids), + ]; + } +} diff --git a/app/Exports/Banking/Sheets/Recurring.php b/app/Exports/Banking/Sheets/Recurring.php new file mode 100644 index 000000000..286a19359 --- /dev/null +++ b/app/Exports/Banking/Sheets/Recurring.php @@ -0,0 +1,36 @@ +cursor(); + } + + public function map($model): array + { + $model->transaction_number = $model->recurable->number; + + return parent::map($model); + } + + public function fields(): array + { + return [ + 'recurable_type', + 'transaction_number', + 'frequency', + 'interval', + 'started_at', + 'status', + 'limit_by', + 'limit_count', + 'auto_send', + ]; + } +} diff --git a/app/Exports/Banking/Sheets/RecurringTransactions.php b/app/Exports/Banking/Sheets/RecurringTransactions.php new file mode 100644 index 000000000..5e4b44ca5 --- /dev/null +++ b/app/Exports/Banking/Sheets/RecurringTransactions.php @@ -0,0 +1,53 @@ +isRecurring()->cursor(); + } + + public function map($model): array + { + $model->account_name = $model->account->name; + $model->contact_email = $model->contact->email; + $model->category_name = $model->category->name; + $model->invoice_bill_number = $model->document->document_number ?? 0; + + return parent::map($model); + } + + public function fields(): array + { + return [ + 'type', + 'number', + 'paid_at', + 'amount', + 'currency_code', + 'currency_rate', + 'account_name', + 'invoice_bill_number', + 'contact_email', + 'category_name', + 'description', + 'payment_method', + 'reference', + 'reconciled', + ]; + } + + public function columnFormats(): array + { + return [ + 'C' => NumberFormat::FORMAT_DATE_YYYYMMDD, + ]; + } +} diff --git a/app/Http/Controllers/Banking/RecurringTransactions.php b/app/Http/Controllers/Banking/RecurringTransactions.php index 2f15bc89c..21ee0314c 100644 --- a/app/Http/Controllers/Banking/RecurringTransactions.php +++ b/app/Http/Controllers/Banking/RecurringTransactions.php @@ -3,7 +3,10 @@ namespace App\Http\Controllers\Banking; use App\Abstracts\Http\Controller; +use App\Exports\Banking\RecurringTransactions as Export; use App\Http\Requests\Banking\Transaction as Request; +use App\Http\Requests\Common\Import as ImportRequest; +use App\Imports\Banking\RecurringTransactions as Import; use App\Jobs\Banking\CreateTransaction; use App\Jobs\Banking\UpdateTransaction; use App\Models\Banking\Account; @@ -97,7 +100,7 @@ class RecurringTransactions extends Controller if ($response['success']) { $response['redirect'] = route('recurring-transactions.show', $response['data']->id); - $message = trans('messages.success.added', ['type' => trans_choice('general.transactions', 1)]); + $message = trans('messages.success.added', ['type' => trans_choice('general.recurring_transactions', 1)]); flash($message)->success(); } else { @@ -122,13 +125,37 @@ class RecurringTransactions extends Controller { $clone = $recurring_transaction->duplicate(); - $message = trans('messages.success.duplicated', ['type' => trans_choice('general.transactions', 1)]); + $message = trans('messages.success.duplicated', ['type' => trans_choice('general.recurring_transactions', 1)]); flash($message)->success(); return redirect()->route('recurring-transactions.edit', $clone->id); } + /** + * Import the specified resource. + * + * @param ImportRequest $request + * + * @return Response + */ + public function import(ImportRequest $request) + { + $response = $this->importExcel(new Import, $request, trans_choice('general.recurring_transactions', 2)); + + if ($response['success']) { + $response['redirect'] = route('recurring-transactions.index'); + + flash($response['message'])->success(); + } else { + $response['redirect'] = route('import.create', ['banking', 'recurring-transactions']); + + flash($response['message'])->error()->important(); + } + + return response()->json($response); + } + /** * Show the form for editing the specified resource. * @@ -174,7 +201,7 @@ class RecurringTransactions extends Controller if ($response['success']) { $response['redirect'] = route('recurring-transactions.show', $recurring_transaction->id); - $message = trans('messages.success.updated', ['type' => trans_choice('general.transactions', 1)]); + $message = trans('messages.success.updated', ['type' => trans_choice('general.recurring_transactions', 1)]); flash($message)->success(); } else { @@ -187,6 +214,16 @@ class RecurringTransactions extends Controller return response()->json($response); } + + /** + * Export the specified resource. + * + * @return Response + */ + public function export() + { + return $this->exportExcel(new Export, trans_choice('general.recurring_transactions', 2)); + } /** * End recurring template. diff --git a/app/Imports/Banking/RecurringTransactions.php b/app/Imports/Banking/RecurringTransactions.php new file mode 100644 index 000000000..22670959f --- /dev/null +++ b/app/Imports/Banking/RecurringTransactions.php @@ -0,0 +1,18 @@ + new Base(), + 'recurring' => new Recurring(), + ]; + } +} diff --git a/app/Imports/Banking/Sheets/Recurring.php b/app/Imports/Banking/Sheets/Recurring.php new file mode 100644 index 000000000..3d9ac9577 --- /dev/null +++ b/app/Imports/Banking/Sheets/Recurring.php @@ -0,0 +1,24 @@ +number($row['transaction_number'])->pluck('id')->first(); + + return $row; + } +} diff --git a/app/Imports/Banking/Sheets/RecurringTransactions.php b/app/Imports/Banking/Sheets/RecurringTransactions.php new file mode 100644 index 000000000..bdec04a77 --- /dev/null +++ b/app/Imports/Banking/Sheets/RecurringTransactions.php @@ -0,0 +1,38 @@ +getCurrencyCode($row); + $row['account_id'] = $this->getAccountId($row); + $row['category_id'] = $this->getCategoryId($row, $transaction_type); + $row['contact_id'] = $this->getContactId($row, $transaction_type); + + if ($transaction_type == 'income') { + $row['document_id'] = Document::invoiceRecurring()->number($row['invoice_bill_number'])->pluck('id')->first(); + } else { + $row['document_id'] = Document::billRecurring()->number($row['invoice_bill_number'])->pluck('id')->first(); + } + + return $row; + } +} diff --git a/resources/views/banking/recurring_transactions/index.blade.php b/resources/views/banking/recurring_transactions/index.blade.php index fad645a4a..fec5c155c 100644 --- a/resources/views/banking/recurring_transactions/index.blade.php +++ b/resources/views/banking/recurring_transactions/index.blade.php @@ -21,6 +21,24 @@ @endcan + + + + more_horiz + + + @can('create-banking-transactions') + + {{ trans('import.import') }} + + @endcan + + + {{ trans('general.export') }} + + + + @if ($transactions->count() || request()->get('search', false)) @@ -141,23 +159,29 @@ @endif From 24a556422aa1f262484a87b5a996bcd6ae8b74fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cihan=20=C5=9Eent=C3=BCrk?= Date: Mon, 14 Aug 2023 17:24:24 +0300 Subject: [PATCH 4/6] added import and export recurring templates route --- routes/admin.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/routes/admin.php b/routes/admin.php index 76703b253..ffa504952 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -80,6 +80,8 @@ Route::group(['prefix' => 'sales'], function () { Route::get('recurring-invoices/{recurring_invoice}/duplicate', 'Sales\RecurringInvoices@duplicate')->name('recurring-invoices.duplicate'); Route::get('recurring-invoices/{recurring_invoice}/end', 'Sales\RecurringInvoices@end')->name('recurring-invoices.end'); + Route::post('recurring-invoices/import', 'Sales\RecurringInvoices@import')->middleware('import')->name('recurring-invoices.import'); + Route::get('recurring-invoices/export', 'Sales\RecurringInvoices@export')->name('recurring-invoices.export'); Route::resource('recurring-invoices', 'Sales\RecurringInvoices', ['middleware' => ['date.format', 'money', 'dropzone']]); Route::get('customers/{customer}/create-invoice', 'Sales\Customers@createInvoice')->name('customers.create-invoice'); @@ -104,6 +106,8 @@ Route::group(['prefix' => 'purchases'], function () { Route::get('recurring-bills/{recurring_bill}/duplicate', 'Purchases\RecurringBills@duplicate')->name('recurring-bills.duplicate'); Route::get('recurring-bills/{recurring_bill}/end', 'Purchases\RecurringBills@end')->name('recurring-bills.end'); + Route::post('recurring-bills/import', 'Purchases\RecurringBills@import')->middleware('import')->name('recurring-bills.import'); + Route::get('recurring-bills/export', 'Purchases\RecurringBills@export')->name('recurring-bills.export'); Route::resource('recurring-bills', 'Purchases\RecurringBills', ['middleware' => ['date.format', 'money', 'dropzone']]); Route::get('vendors/{vendor}/create-bill', 'Purchases\Vendors@createBill')->name('vendors.create-bill'); @@ -142,6 +146,8 @@ Route::group(['prefix' => 'banking'], function () { Route::get('recurring-transactions/{recurring_transaction}/duplicate', 'Banking\RecurringTransactions@duplicate')->name('recurring-transactions.duplicate'); Route::get('recurring-transactions/{recurring_transaction}/end', 'Banking\RecurringTransactions@end')->name('recurring-transactions.end'); + Route::post('recurring-transactions/import', 'Banking\RecurringTransactions@import')->middleware('import')->name('recurring-transactions.import'); + Route::get('recurring-transactions/export', 'Banking\RecurringTransactions@export')->name('recurring-transactions.export'); Route::resource('recurring-transactions', 'Banking\RecurringTransactions', ['middleware' => ['date.format', 'money', 'dropzone']]); Route::get('transfers/{transfer}/print', 'Banking\Transfers@printTransfer')->name('transfers.print'); From 90c3e22ff159327dc03b4930f0556a2435d9f9d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cihan=20=C5=9Eent=C3=BCrk?= Date: Mon, 14 Aug 2023 17:27:35 +0300 Subject: [PATCH 5/6] added sample recurring import file --- public/files/import/recurring-bills.xlsx | Bin 0 -> 14200 bytes public/files/import/recurring-invoices.xlsx | Bin 0 -> 14236 bytes public/files/import/recurring-transactions.xlsx | Bin 0 -> 8275 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/files/import/recurring-bills.xlsx create mode 100644 public/files/import/recurring-invoices.xlsx create mode 100644 public/files/import/recurring-transactions.xlsx diff --git a/public/files/import/recurring-bills.xlsx b/public/files/import/recurring-bills.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..9bbc38bc62d1129fc02056d1ccaf415f186db845 GIT binary patch literal 14200 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 C>yn)S literal 0 HcmV?d00001 diff --git a/public/files/import/recurring-invoices.xlsx b/public/files/import/recurring-invoices.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..eea3bc376dc90b2930266ef4709136dee9961556 GIT binary patch literal 14236 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 Be>MOB literal 0 HcmV?d00001 diff --git a/public/files/import/recurring-transactions.xlsx b/public/files/import/recurring-transactions.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..0adc49828e3d1e4a0866ca31c8d16022cad74109 GIT binary patch literal 8275 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≧Aqn~af$-lst^!6u{bL-!yUY4+9!}iB0D%7iWr Date: Wed, 16 Aug 2023 16:37:59 +0300 Subject: [PATCH 6/6] Updated export file paths --- app/BulkActions/Purchases/Bills.php | 2 +- app/BulkActions/Sales/Invoices.php | 2 +- tests/Feature/Purchases/BillsTest.php | 2 +- tests/Feature/Sales/InvoicesTest.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/BulkActions/Purchases/Bills.php b/app/BulkActions/Purchases/Bills.php index 8d65a3277..c48e94d74 100644 --- a/app/BulkActions/Purchases/Bills.php +++ b/app/BulkActions/Purchases/Bills.php @@ -5,7 +5,7 @@ namespace App\BulkActions\Purchases; use App\Abstracts\BulkAction; use App\Events\Document\DocumentCancelled; use App\Events\Document\DocumentReceived; -use App\Exports\Purchases\Bills as Export; +use App\Exports\Purchases\Bills\Bills as Export; use App\Jobs\Banking\CreateBankingDocumentTransaction; use App\Jobs\Document\CreateDocumentHistory; use App\Jobs\Document\DeleteDocument; diff --git a/app/BulkActions/Sales/Invoices.php b/app/BulkActions/Sales/Invoices.php index 07e78051b..a079531d5 100644 --- a/app/BulkActions/Sales/Invoices.php +++ b/app/BulkActions/Sales/Invoices.php @@ -7,7 +7,7 @@ use App\Events\Document\DocumentCancelled; use App\Events\Document\DocumentCreated; use App\Events\Document\DocumentMarkedSent; use App\Events\Document\PaymentReceived; -use App\Exports\Sales\Invoices as Export; +use App\Exports\Sales\Invoices\Invoices as Export; use App\Jobs\Document\DeleteDocument; use App\Models\Document\Document; diff --git a/tests/Feature/Purchases/BillsTest.php b/tests/Feature/Purchases/BillsTest.php index bd03a11ba..c879a2ea2 100644 --- a/tests/Feature/Purchases/BillsTest.php +++ b/tests/Feature/Purchases/BillsTest.php @@ -2,7 +2,7 @@ namespace Tests\Feature\Purchases; -use App\Exports\Purchases\Bills as Export; +use App\Exports\Purchases\Bills\Bills as Export; use App\Jobs\Document\CreateDocument; use App\Models\Document\Document; use Illuminate\Http\UploadedFile; diff --git a/tests/Feature/Sales/InvoicesTest.php b/tests/Feature/Sales/InvoicesTest.php index e38d1ba26..3aad6da17 100644 --- a/tests/Feature/Sales/InvoicesTest.php +++ b/tests/Feature/Sales/InvoicesTest.php @@ -2,7 +2,7 @@ namespace Tests\Feature\Sales; -use App\Exports\Sales\Invoices as Export; +use App\Exports\Sales\Invoices\Invoices as Export; use App\Jobs\Document\CreateDocument; use App\Models\Document\Document; use App\Notifications\Sale\Invoice as Notification;