diff --git a/app/Export/CSV/BaseExport.php b/app/Export/CSV/BaseExport.php index d9731c49b680..e98b4bbeeeff 100644 --- a/app/Export/CSV/BaseExport.php +++ b/app/Export/CSV/BaseExport.php @@ -42,6 +42,37 @@ class BaseExport public array $forced_keys = []; + protected array $vendor_report_keys = [ + 'address1' => 'vendor.address1', + 'address2' => 'vendor.address2', + 'city' => 'vendor.city', + 'country' => 'vendor.country_id', + 'custom_value1' => 'vendor.custom_value1', + 'custom_value2' => 'vendor.custom_value2', + 'custom_value3' => 'vendor.custom_value3', + 'custom_value4' => 'vendor.custom_value4', + 'id_number' => 'vendor.id_number', + 'name' => 'vendor.name', + 'number' => 'vendor.number', + 'client_phone' => 'vendor.phone', + 'postal_code' => 'vendor.postal_code', + 'private_notes' => 'vendor.private_notes', + 'public_notes' => 'vendor.public_notes', + 'state' => 'vendor.state', + 'vat_number' => 'vendor.vat_number', + 'website' => 'vendor.website', + 'currency' => 'vendor.currency', + 'first_name' => 'vendor_contact.first_name', + 'last_name' => 'vendor_contact.last_name', + 'contact_phone' => 'vendor_contact.phone', + 'contact_custom_value1' => 'vendor_contact.custom_value1', + 'contact_custom_value2' => 'vendor_contact.custom_value2', + 'contact_custom_value3' => 'vendor_contact.custom_value3', + 'contact_custom_value4' => 'vendor_contact.custom_value4', + 'email' => 'vendor_contact.email', + 'status' => 'vendor.status', + ]; + protected array $client_report_keys = [ "name" => "client.name", "user" => "client.user_id", @@ -103,6 +134,42 @@ class BaseExport "user" => "invoice.user_id", ]; + protected array $purchase_order_report_keys = [ + 'amount' => 'purchase_order.amount', + 'balance' => 'purchase_order.balance', + 'vendor' => 'purchase_order.vendor_id', + // 'custom_surcharge1' => 'purchase_order.custom_surcharge1', + // 'custom_surcharge2' => 'purchase_order.custom_surcharge2', + // 'custom_surcharge3' => 'purchase_order.custom_surcharge3', + // 'custom_surcharge4' => 'purchase_order.custom_surcharge4', + 'custom_value1' => 'purchase_order.custom_value1', + 'custom_value2' => 'purchase_order.custom_value2', + 'custom_value3' => 'purchase_order.custom_value3', + 'custom_value4' => 'purchase_order.custom_value4', + 'date' => 'purchase_order.date', + 'discount' => 'purchase_order.discount', + 'due_date' => 'purchase_order.due_date', + 'exchange_rate' => 'purchase_order.exchange_rate', + 'footer' => 'purchase_order.footer', + 'number' => 'purchase_order.number', + 'paid_to_date' => 'purchase_order.paid_to_date', + 'partial' => 'purchase_order.partial', + 'partial_due_date' => 'purchase_order.partial_due_date', + 'po_number' => 'purchase_order.po_number', + 'private_notes' => 'purchase_order.private_notes', + 'public_notes' => 'purchase_order.public_notes', + 'status' => 'purchase_order.status_id', + 'tax_name1' => 'purchase_order.tax_name1', + 'tax_name2' => 'purchase_order.tax_name2', + 'tax_name3' => 'purchase_order.tax_name3', + 'tax_rate1' => 'purchase_order.tax_rate1', + 'tax_rate2' => 'purchase_order.tax_rate2', + 'tax_rate3' => 'purchase_order.tax_rate3', + 'terms' => 'purchase_order.terms', + 'total_taxes' => 'purchase_order.total_taxes', + 'currency_id' => 'purchase_order.currency_id', + ]; + protected array $item_report_keys = [ "quantity" => "item.quantity", "cost" => "item.cost", @@ -222,7 +289,10 @@ class BaseExport match($parts[0]) { 'contact' => $value = $this->resolveClientContactKey($parts[1], $entity, $transformer), 'client' => $value = $this->resolveClientKey($parts[1], $entity, $transformer), + 'vendor' => $value = $this->resolveVendorKey($parts[1], $entity, $transformer), + 'vendor_contact' => $value = $this->resolveVendorContactKey($parts[1], $entity, $transformer), 'invoice' => $value = $this->resolveInvoiceKey($parts[1], $entity, $transformer), + 'purchase_order' => $value = $this->resolvePurchaseOrderKey($parts[1], $entity, $transformer), 'payment' => $value = $this->resolvePaymentKey($parts[1], $entity, $transformer), default => $value = '' }; @@ -239,6 +309,51 @@ class BaseExport } + private function resolveVendorContactKey($column, $entity, $transformer) + { + + $primary_contact = $entity->vendor->primary_contact()->first() ?? $entity->vendor->contacts()->first(); + + return $primary_contact?->{$column} ?? ''; + + } + + private function resolveVendorKey($column, $entity, $transformer) + { + $transformed_entity = $transformer->includeVendor($entity); + + $manager = new Manager(); + $manager->setSerializer(new ArraySerializer()); + $transformed_entity = $manager->createData($transformed_entity)->toArray(); + + if($column == 'name') + return $transformed_entity['display_name']; + + if($column == 'user_id') + return $entity->vendor->user->present()->name(); + + if($column == 'country_id') + return $entity->vendor->country ? ctrans("texts.country_{$entity->vendor->country->name}") : ''; + + if ($column == 'currency_id') { + return $entity->vendor->currency() ? $entity->vendor->currency()->code : $entity->company->currency()->code; + } + + if($column == 'status') + return $entity->stringStatus($entity->status_id); + + + + if(array_key_exists($column, $transformed_entity)) + return $transformed_entity[$column]; + + nlog("export: Could not resolve vendor key: {$column}"); + + return ''; + + } + + private function resolveClientKey($column, $entity, $transformer) { $transformed_client = $transformer->includeClient($entity); @@ -282,6 +397,18 @@ class BaseExport } + private function resolvePurchaseOrderKey($column, $entity, $transformer) + { + nlog("searching for {$column}"); + + $transformed_entity = $transformer->transform($entity); + + if($column == 'status') + return $entity->stringStatus($entity->status_id); + + return ''; + } + private function resolveInvoiceKey($column, $entity, $transformer) { nlog("searching for {$column}"); @@ -495,6 +622,8 @@ class BaseExport public function buildHeader() :array { $header = []; + + nlog($this->input['report_keys']); foreach (array_merge($this->input['report_keys'], $this->forced_keys) as $value) { @@ -538,8 +667,13 @@ class BaseExport } $key = str_replace('item.', '', $key); + $key = str_replace('recurring_invoice.', '', $key); $key = str_replace('invoice.', '', $key); + $key = str_replace('quote.', '', $key); + $key = str_replace('credit.', '', $key); + $key = str_replace('task.', '', $key); $key = str_replace('client.', '', $key); + $key = str_replace('vendor.', '', $key); $key = str_replace('contact.', '', $key); $key = str_replace('payment.', '', $key); diff --git a/app/Export/CSV/ClientExport.php b/app/Export/CSV/ClientExport.php index 1f45c2b97569..c41dcc7a63df 100644 --- a/app/Export/CSV/ClientExport.php +++ b/app/Export/CSV/ClientExport.php @@ -185,7 +185,7 @@ class ClientExport extends BaseExport } if ($client->deleted_at) { - return ctrans('texts.arcvived'); + return ctrans('texts.archived'); } return ctrans('texts.active'); diff --git a/app/Export/CSV/PurchaseOrderExport.php b/app/Export/CSV/PurchaseOrderExport.php new file mode 100644 index 000000000000..0b4202ce5af0 --- /dev/null +++ b/app/Export/CSV/PurchaseOrderExport.php @@ -0,0 +1,172 @@ + 'amount', + 'balance' => 'balance', + 'vendor' => 'vendor_id', + 'custom_surcharge1' => 'custom_surcharge1', + 'custom_surcharge2' => 'custom_surcharge2', + 'custom_surcharge3' => 'custom_surcharge3', + 'custom_surcharge4' => 'custom_surcharge4', + 'custom_value1' => 'custom_value1', + 'custom_value2' => 'custom_value2', + 'custom_value3' => 'custom_value3', + 'custom_value4' => 'custom_value4', + 'date' => 'date', + 'discount' => 'discount', + 'due_date' => 'due_date', + 'exchange_rate' => 'exchange_rate', + 'footer' => 'footer', + 'number' => 'number', + 'paid_to_date' => 'paid_to_date', + 'partial' => 'partial', + 'partial_due_date' => 'partial_due_date', + 'po_number' => 'po_number', + 'private_notes' => 'private_notes', + 'public_notes' => 'public_notes', + 'status' => 'status_id', + 'tax_name1' => 'tax_name1', + 'tax_name2' => 'tax_name2', + 'tax_name3' => 'tax_name3', + 'tax_rate1' => 'tax_rate1', + 'tax_rate2' => 'tax_rate2', + 'tax_rate3' => 'tax_rate3', + 'terms' => 'terms', + 'total_taxes' => 'total_taxes', + 'currency_id' => 'currency_id', + ]; + + private array $decorate_keys = [ + 'country', + 'currency_id', + 'status', + 'vendor', + 'project', + ]; + + + public function __construct(Company $company, array $input) + { + $this->company = $company; + $this->input = $input; + $this->purchase_order_transformer = new PurchaseOrderTransformer(); + } + + public function run() + { + MultiDB::setDb($this->company->db); + App::forgetInstance('translator'); + App::setLocale($this->company->locale()); + $t = app('translator'); + $t->replace(Ninja::transformTranslations($this->company->settings)); + + //load the CSV document from a string + $this->csv = Writer::createFromString(); + + if (count($this->input['report_keys']) == 0) { + $this->input['report_keys'] = array_values($this->entity_keys); + } + + //insert the header + $this->csv->insertOne($this->buildHeader()); + + $query = PurchaseOrder::query() + ->withTrashed() + ->with('vendor') + ->where('company_id', $this->company->id) + ->where('is_deleted', 0); + + $query = $this->addDateRange($query); + + // if(isset($this->input['status'])) { + // $query = $this->addPurchaseOrderStatusFilter($query, $this->input['status']); + // } + + $query->cursor() + ->each(function ($purchase_order) { + $this->csv->insertOne($this->buildRow($purchase_order)); + }); + + return $this->csv->toString(); + } + + private function buildRow(PurchaseOrder $purchase_order) :array + { + $transformed_purchase_order = $this->purchase_order_transformer->transform($purchase_order); + + $entity = []; + + foreach (array_values($this->input['report_keys']) as $key) { + $keyval = array_search($key, $this->entity_keys); + + if(!$keyval) { + $keyval = array_search(str_replace("invoice.", "", $key), $this->entity_keys) ?? $key; + } + + if(!$keyval) { + $keyval = $key; + } + + if (array_key_exists($key, $transformed_purchase_order)) { + $entity[$keyval] = $transformed_purchase_order[$key]; + } elseif (array_key_exists($keyval, $transformed_purchase_order)) { + $entity[$keyval] = $transformed_purchase_order[$keyval]; + } else { + $entity[$keyval] = $this->resolveKey($keyval, $purchase_order, $this->purchase_order_transformer); + } + } + + return $this->decorateAdvancedFields($purchase_order, $entity); + } + + private function decorateAdvancedFields(PurchaseOrder $purchase_order, array $entity) :array + { + if (in_array('country_id', $this->input['report_keys'])) { + $entity['country'] = $purchase_order->vendor->country ? ctrans("texts.country_{$purchase_order->vendor->country->name}") : ''; + } + + if (in_array('currency_id', $this->input['report_keys'])) { + $entity['currency_id'] = $purchase_order->vendor->currency() ? $purchase_order->vendor->currency()->code : $purchase_order->company->currency()->code; + } + + if (in_array('vendor_id', $this->input['report_keys'])) { + $entity['vendor'] = $purchase_order->vendor->present()->name(); + } + + if (in_array('status_id', $this->input['report_keys'])) { + $entity['status'] = $purchase_order->stringStatus($purchase_order->status_id); + } + + return $entity; + } +} diff --git a/app/Export/CSV/PurchaseOrderItemExport.php b/app/Export/CSV/PurchaseOrderItemExport.php new file mode 100644 index 000000000000..4847a162f9c0 --- /dev/null +++ b/app/Export/CSV/PurchaseOrderItemExport.php @@ -0,0 +1,246 @@ + 'amount', + 'balance' => 'balance', + 'vendor' => 'vendor_id', + 'vendor_number' => 'vendor.number', + 'vendor_id_number' => 'vendor.id_number', + 'custom_surcharge1' => 'custom_surcharge1', + 'custom_surcharge2' => 'custom_surcharge2', + 'custom_surcharge3' => 'custom_surcharge3', + 'custom_surcharge4' => 'custom_surcharge4', + // 'custom_value1' => 'custom_value1', + // 'custom_value2' => 'custom_value2', + // 'custom_value3' => 'custom_value3', + // 'custom_value4' => 'custom_value4', + 'date' => 'date', + 'discount' => 'discount', + 'due_date' => 'due_date', + 'exchange_rate' => 'exchange_rate', + 'footer' => 'footer', + 'number' => 'number', + 'paid_to_date' => 'paid_to_date', + 'partial' => 'partial', + 'partial_due_date' => 'partial_due_date', + 'po_number' => 'po_number', + 'private_notes' => 'private_notes', + 'public_notes' => 'public_notes', + 'status' => 'status_id', + 'tax_name1' => 'tax_name1', + 'tax_name2' => 'tax_name2', + 'tax_name3' => 'tax_name3', + 'tax_rate1' => 'tax_rate1', + 'tax_rate2' => 'tax_rate2', + 'tax_rate3' => 'tax_rate3', + 'terms' => 'terms', + 'total_taxes' => 'total_taxes', + 'currency' => 'currency_id', + 'quantity' => 'item.quantity', + 'cost' => 'item.cost', + 'product_key' => 'item.product_key', + 'buy_price' => 'item.product_cost', + 'notes' => 'item.notes', + 'discount' => 'item.discount', + 'is_amount_discount' => 'item.is_amount_discount', + 'tax_rate1' => 'item.tax_rate1', + 'tax_rate2' => 'item.tax_rate2', + 'tax_rate3' => 'item.tax_rate3', + 'tax_name1' => 'item.tax_name1', + 'tax_name2' => 'item.tax_name2', + 'tax_name3' => 'item.tax_name3', + 'line_total' => 'item.line_total', + 'gross_line_total' => 'item.gross_line_total', + // 'invoice1' => 'item.custom_value1', + // 'invoice2' => 'item.custom_value2', + // 'invoice3' => 'item.custom_value3', + // 'invoice4' => 'item.custom_value4', + 'tax_category' => 'item.tax_id', + 'type' => 'item.type_id', + ]; + + private array $decorate_keys = [ + 'client', + 'currency_id', + 'status' + ]; + + public function __construct(Company $company, array $input) + { + $this->company = $company; + $this->input = $input; + $this->purchase_order_transformer = new PurchaseOrderTransformer(); + } + + public function run() + { + MultiDB::setDb($this->company->db); + App::forgetInstance('translator'); + App::setLocale($this->company->locale()); + $t = app('translator'); + $t->replace(Ninja::transformTranslations($this->company->settings)); + + //load the CSV document from a string + $this->csv = Writer::createFromString(); + + if (count($this->input['report_keys']) == 0) { + $this->force_keys = true; + $this->input['report_keys'] = array_values($this->entity_keys); + } + + //insert the header + $this->csv->insertOne($this->buildHeader()); + + $query = PurchaseOrder::query() + ->withTrashed() + ->with('vendor')->where('company_id', $this->company->id) + ->where('is_deleted', 0); + + $query = $this->addDateRange($query); + + $query->cursor() + ->each(function ($purchase_order) { + $this->iterateItems($purchase_order); + }); + + return $this->csv->toString(); + } + + private function iterateItems(PurchaseOrder $purchase_order) + { + $transformed_invoice = $this->buildRow($purchase_order); + + $transformed_items = []; + + foreach ($purchase_order->line_items as $item) { + $item_array = []; + + foreach (array_values($this->input['report_keys']) as $key) { //items iterator produces item array + + if (str_contains($key, "item.")) { + + $key = str_replace("item.", "", $key); + + $keyval = $key; + + $keyval = str_replace("custom_value", "invoice", $key); + + if($key == 'type_id') { + $keyval = 'type'; + } + + if($key == 'tax_id') { + $keyval = 'tax_category'; + } + + if (property_exists($item, $key)) { + $item_array[$keyval] = $item->{$key}; + } else { + $item_array[$keyval] = ''; + } + } + } + + $entity = []; + + foreach (array_values($this->input['report_keys']) as $key) { //create an array of report keys only + $keyval = array_search($key, $this->entity_keys); + + if (array_key_exists($key, $transformed_items)) { + $entity[$keyval] = $transformed_items[$key]; + } else { + $entity[$keyval] = ""; + } + } + + $transformed_items = array_merge($transformed_invoice, $item_array); + $entity = $this->decorateAdvancedFields($purchase_order, $transformed_items); + + $this->csv->insertOne($entity); + } + } + + private function buildRow(PurchaseOrder $purchase_order) :array + { + $transformed_invoice = $this->purchase_order_transformer->transform($purchase_order); + + $entity = []; + + foreach (array_values($this->input['report_keys']) as $key) { + $keyval = array_search($key, $this->entity_keys); + + if(!$keyval) { + $keyval = array_search(str_replace("invoice.", "", $key), $this->entity_keys) ?? $key; + } + + if(!$keyval) { + $keyval = $key; + } + + if (array_key_exists($key, $transformed_invoice)) { + $entity[$keyval] = $transformed_invoice[$key]; + } elseif (array_key_exists($keyval, $transformed_invoice)) { + $entity[$keyval] = $transformed_invoice[$keyval]; + } else { + $entity[$keyval] = $this->resolveKey($keyval, $purchase_order, $this->purchase_order_transformer); + } + } + + return $this->decorateAdvancedFields($purchase_order, $entity); + } + + private function decorateAdvancedFields(PurchaseOrder $purchase_order, array $entity) :array + { + if (in_array('currency_id', $this->input['report_keys'])) { + $entity['currency'] = $purchase_order->vendor->currency() ? $purchase_order->vendor->currency()->code : $purchase_order->company->currency()->code; + } + + if(array_key_exists('type', $entity)) { + $entity['type'] = $purchase_order->typeIdString($entity['type']); + } + + if(array_key_exists('tax_category', $entity)) { + $entity['tax_category'] = $purchase_order->taxTypeString($entity['tax_category']); + } + + if($this->force_keys) { + $entity['vendor'] = $purchase_order->vendor->present()->name(); + $entity['vendor_id_number'] = $purchase_order->vendor->id_number; + $entity['vendor_number'] = $purchase_order->vendor->number; + $entity['status'] = $purchase_order->stringStatus($purchase_order->status_id); + } + + return $entity; + } +} diff --git a/app/Export/CSV/VendorExport.php b/app/Export/CSV/VendorExport.php new file mode 100644 index 000000000000..872b818ef762 --- /dev/null +++ b/app/Export/CSV/VendorExport.php @@ -0,0 +1,172 @@ + 'vendor.address1', + 'address2' => 'vendor.address2', + 'city' => 'vendor.city', + 'country' => 'vendor.country_id', + 'custom_value1' => 'vendor.custom_value1', + 'custom_value2' => 'vendor.custom_value2', + 'custom_value3' => 'vendor.custom_value3', + 'custom_value4' => 'vendor.custom_value4', + 'id_number' => 'vendor.id_number', + 'name' => 'vendor.name', + 'number' => 'vendor.number', + 'client_phone' => 'vendor.phone', + 'postal_code' => 'vendor.postal_code', + 'private_notes' => 'vendor.private_notes', + 'public_notes' => 'vendor.public_notes', + 'state' => 'vendor.state', + 'vat_number' => 'vendor.vat_number', + 'website' => 'vendor.website', + 'currency' => 'vendor.currency', + 'first_name' => 'vendor_contact.first_name', + 'last_name' => 'vendor_contact.last_name', + 'contact_phone' => 'vendor_contact.phone', + 'contact_custom_value1' => 'vendor_contact.custom_value1', + 'contact_custom_value2' => 'vendor_contact.custom_value2', + 'contact_custom_value3' => 'vendor_contact.custom_value3', + 'contact_custom_value4' => 'vendor_contact.custom_value4', + 'email' => 'vendor_contact.email', + 'status' => 'vendor.status' + ]; + + private array $decorate_keys = [ + 'vendor.country_id', + 'vendor.currency', + ]; + + public array $forced_keys = [ + // 'vendor.status' + ]; + + public function __construct(Company $company, array $input) + { + $this->company = $company; + $this->input = $input; + $this->vendor_transformer = new VendorTransformer(); + $this->contact_transformer = new VendorContactTransformer(); + } + + public function run() + { + MultiDB::setDb($this->company->db); + App::forgetInstance('translator'); + App::setLocale($this->company->locale()); + $t = app('translator'); + $t->replace(Ninja::transformTranslations($this->company->settings)); + + //load the CSV document from a string + $this->csv = Writer::createFromString(); + + if (count($this->input['report_keys']) == 0) { + $this->input['report_keys'] = array_values($this->entity_keys); + } + + //insert the header + $this->csv->insertOne($this->buildHeader()); + + $query = Vendor::query()->with('contacts') + ->withTrashed() + ->where('company_id', $this->company->id) + ->where('is_deleted', 0); + + $query = $this->addDateRange($query); + + $query->cursor() + ->each(function ($vendor) { + $this->csv->insertOne($this->buildRow($vendor)); + }); + + return $this->csv->toString(); + } + + private function buildRow(Vendor $vendor) :array + { + $transformed_contact = false; + + $transformed_vendor = $this->vendor_transformer->transform($vendor); + + if ($contact = $vendor->contacts()->first()) { + $transformed_contact = $this->contact_transformer->transform($contact); + } + + $entity = []; + + foreach (array_values($this->input['report_keys']) as $key) { + $parts = explode('.', $key); + + $keyval = array_search($key, $this->entity_keys); + + if (is_array($parts) && $parts[0] == 'vendor' && array_key_exists($parts[1], $transformed_vendor)) { + $entity[$keyval] = $transformed_vendor[$parts[1]]; + } elseif (is_array($parts) && $parts[0] == 'vendor_contact' && array_key_exists($parts[1], $transformed_contact)) { + $entity[$keyval] = $transformed_contact[$parts[1]]; + } else { + $entity[$keyval] = ''; + } + } + + return $this->decorateAdvancedFields($vendor, $entity); + } + + private function decorateAdvancedFields(Vendor $vendor, array $entity) :array + { + if (in_array('vendor.country_id', $this->input['report_keys'])) { + $entity['country'] = $vendor->country ? ctrans("texts.country_{$vendor->country->name}") : ''; + } + + if (in_array('vendor.currency', $this->input['report_keys'])) { + $entity['currency'] = $vendor->currency() ? $vendor->currency()->code : $vendor->company->currency()->code; + } + + $entity['status'] = $this->calculateStatus($vendor); + + return $entity; + } + + private function calculateStatus($vendor) + { + if ($vendor->is_deleted) { + return ctrans('texts.deleted'); + } + + if ($vendor->deleted_at) { + return ctrans('texts.archived'); + } + + return ctrans('texts.active'); + } +} diff --git a/app/Http/Controllers/Reports/RecurringInvoiceReportController.php b/app/Http/Controllers/Reports/RecurringInvoiceReportController.php index 01d9a0f1f72a..eaae55bd1351 100644 --- a/app/Http/Controllers/Reports/RecurringInvoiceReportController.php +++ b/app/Http/Controllers/Reports/RecurringInvoiceReportController.php @@ -29,37 +29,6 @@ class RecurringInvoiceReportController extends BaseController parent::__construct(); } - /** - * @OA\Post( - * path="/api/v1/reports/recurring_invoices", - * operationId="getRecurringInvoiceReport", - * tags={"reports"}, - * summary="Recurring Invoice reports", - * description="Export recurring invoice reports", - * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), - * @OA\RequestBody( - * required=true, - * @OA\JsonContent(ref="#/components/schemas/GenericReportSchema") - * ), - * @OA\Response( - * response=200, - * description="success", - * @OA\Header(header="X-MINIMUM-CLIENT-VERSION", ref="#/components/headers/X-MINIMUM-CLIENT-VERSION"), - * @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"), - * @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"), - * ), - * @OA\Response( - * response=422, - * description="Validation error", - * @OA\JsonContent(ref="#/components/schemas/ValidationError"), - * ), - * @OA\Response( - * response="default", - * description="Unexpected Error", - * @OA\JsonContent(ref="#/components/schemas/Error"), - * ), - * ) - */ public function __invoke(GenericReportRequest $request) { if ($request->has('send_email') && $request->get('send_email')) { diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php index e7eeae12c41a..84ca29d6f31d 100644 --- a/app/Models/Vendor.php +++ b/app/Models/Vendor.php @@ -108,50 +108,6 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $contacts * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $contacts - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $contacts - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $contacts - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $contacts - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $contacts - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $contacts - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $contacts - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $contacts - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $contacts - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $contacts - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $contacts - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact * @mixin \Eloquent */ class Vendor extends BaseModel diff --git a/lang/en/texts.php b/lang/en/texts.php index d17b5e0ef1fb..de8f17ea971e 100644 --- a/lang/en/texts.php +++ b/lang/en/texts.php @@ -5123,6 +5123,7 @@ $LANG = array( 'currency_swazi_lilangeni' => 'Swazi Lilangeni', 'income' => 'Income', 'amount_received_help' => 'Enter a value here if the total amount received was MORE than the invoice amount, or when recording a payment with no invoices. Otherwise this field should be left blank.', + 'vendor_phone' => 'Vendor Phone', ); diff --git a/routes/api.php b/routes/api.php index b2ef6d3d5a15..97c8550b3417 100644 --- a/routes/api.php +++ b/routes/api.php @@ -75,6 +75,7 @@ use App\Http\Controllers\HostedMigrationController; use App\Http\Controllers\ConnectedAccountController; use App\Http\Controllers\RecurringExpenseController; use App\Http\Controllers\RecurringInvoiceController; +use App\Http\Controllers\ProtectedDownloadController; use App\Http\Controllers\ClientGatewayTokenController; use App\Http\Controllers\Reports\TaskReportController; use App\Http\Controllers\Auth\ForgotPasswordController; @@ -85,6 +86,7 @@ use App\Http\Controllers\Auth\PasswordTimeoutController; use App\Http\Controllers\PreviewPurchaseOrderController; use App\Http\Controllers\Reports\ClientReportController; use App\Http\Controllers\Reports\CreditReportController; +use App\Http\Controllers\Reports\VendorReportController; use App\Http\Controllers\Reports\ExpenseReportController; use App\Http\Controllers\Reports\InvoiceReportController; use App\Http\Controllers\Reports\PaymentReportController; @@ -101,11 +103,12 @@ use App\Http\Controllers\Support\Messages\SendingController; use App\Http\Controllers\Reports\ClientSalesReportController; use App\Http\Controllers\Reports\InvoiceItemReportController; use App\Http\Controllers\PaymentNotificationWebhookController; -use App\Http\Controllers\ProtectedDownloadController; use App\Http\Controllers\Reports\ProductSalesReportController; use App\Http\Controllers\Reports\ClientBalanceReportController; use App\Http\Controllers\Reports\ClientContactReportController; +use App\Http\Controllers\Reports\PurchaseOrderReportController; use App\Http\Controllers\Reports\RecurringInvoiceReportController; +use App\Http\Controllers\Reports\PurchaseOrderItemReportController; Route::group(['middleware' => ['throttle:api', 'api_secret_check']], function () { Route::post('api/v1/signup', [AccountController::class, 'store'])->name('signup.submit'); @@ -272,7 +275,6 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale'] Route::post('recurring_expenses/bulk', [RecurringExpenseController::class, 'bulk'])->name('recurring_expenses.bulk'); Route::put('recurring_expenses/{recurring_expense}/upload', [RecurringExpenseController::class, 'upload']); - Route::resource('recurring_invoices', RecurringInvoiceController::class); // name = (recurring_invoices. index / create / show / update / destroy / edit Route::post('recurring_invoices/bulk', [RecurringInvoiceController::class, 'bulk'])->name('recurring_invoices.bulk'); Route::put('recurring_invoices/{recurring_invoice}/upload', [RecurringInvoiceController::class, 'upload']); @@ -291,6 +293,8 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale'] Route::post('reports/expenses', ExpenseReportController::class)->middleware('throttle:20,1'); Route::post('reports/invoices', InvoiceReportController::class)->middleware('throttle:20,1'); Route::post('reports/invoice_items', InvoiceItemReportController::class)->middleware('throttle:20,1'); + Route::post('reports/purchase_orders', PurchaseOrderReportController::class)->middleware('throttle:20,1'); + Route::post('reports/purchase_order_items', PurchaseOrderItemReportController::class)->middleware('throttle:20,1'); Route::post('reports/quotes', QuoteReportController::class)->middleware('throttle:20,1'); Route::post('reports/quote_items', QuoteItemReportController::class)->middleware('throttle:20,1'); Route::post('reports/recurring_invoices', RecurringInvoiceReportController::class)->middleware('throttle:20,1'); @@ -298,7 +302,7 @@ Route::group(['middleware' => ['throttle:api', 'api_db', 'token_auth', 'locale'] Route::post('reports/products', ProductReportController::class)->middleware('throttle:20,1'); Route::post('reports/product_sales', ProductSalesReportController::class)->middleware('throttle:20,1'); Route::post('reports/tasks', TaskReportController::class)->middleware('throttle:20,1'); - + Route::post('reports/vendors', VendorReportController::class)->middleware('throttle:20,1'); Route::post('reports/profitloss', ProfitAndLossController::class); Route::post('reports/ar_detail_report', ARDetailReportController::class); Route::post('reports/ar_summary_report', ARSummaryReportController::class); diff --git a/tests/Feature/Export/ClientCsvTest.php b/tests/Feature/Export/ClientCsvTest.php index 5e3ef755a660..5361b946b532 100644 --- a/tests/Feature/Export/ClientCsvTest.php +++ b/tests/Feature/Export/ClientCsvTest.php @@ -37,6 +37,54 @@ class ClientCsvTest extends TestCase $this->withoutExceptionHandling(); } + public function testRecurringInvoiceExportCsv() + { + $data = [ + 'date_range' => 'this_year', + 'report_keys' => [], + 'send_email' => false, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/reports/recurring_invoices', $data); + + $response->assertStatus(200); + } + + public function testVendorExportCsv() + { + $data = [ + 'date_range' => 'this_year', + 'report_keys' => [], + 'send_email' => false, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/reports/vendors', $data); + + $response->assertStatus(200); + } + + public function testPurchaseOrderExportCsv() + { + $data = [ + 'date_range' => 'this_year', + 'report_keys' => [], + 'send_email' => false, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/reports/purchase_orders', $data); + + $response->assertStatus(200); + } + public function testClientExportCsv() { $data = [