diff --git a/app/Export/CSV/BaseExport.php b/app/Export/CSV/BaseExport.php index a8fed6fff9..26a6c4b2ae 100644 --- a/app/Export/CSV/BaseExport.php +++ b/app/Export/CSV/BaseExport.php @@ -452,6 +452,8 @@ class BaseExport 'billable' => 'task.billable', 'item_notes' => 'task.item_notes', 'time_log' => 'task.time_log', + 'user' => 'task.user_id', + 'assigned_user' => 'task.assigned_user_id', ]; protected array $forced_client_fields = [ diff --git a/app/Export/Decorators/TaskDecorator.php b/app/Export/Decorators/TaskDecorator.php index 277a7a4226..7cf811f468 100644 --- a/app/Export/Decorators/TaskDecorator.php +++ b/app/Export/Decorators/TaskDecorator.php @@ -22,7 +22,6 @@ class TaskDecorator extends Decorator implements DecoratorInterface public function transform(string $key, mixed $entity): mixed { $task = false; - if($entity instanceof Task) { $task = $entity; } elseif($entity->task) { @@ -131,5 +130,13 @@ class TaskDecorator extends Decorator implements DecoratorInterface return $task->project()->exists() ? $task->project->name : ''; } + public function assigned_user_id(Task $task) + { + return $task->assigned_user ? $task->assigned_user->present()->name() : ''; + } + public function user_id(Task $task) + { + return $task->user ? $task->user->present()->name() : ''; + } } diff --git a/app/Http/Requests/Expense/UpdateExpenseRequest.php b/app/Http/Requests/Expense/UpdateExpenseRequest.php index 812449e30b..c8de24ea40 100644 --- a/app/Http/Requests/Expense/UpdateExpenseRequest.php +++ b/app/Http/Requests/Expense/UpdateExpenseRequest.php @@ -78,6 +78,9 @@ class UpdateExpenseRequest extends Request $input['currency_id'] = (string) $user->company()->settings->currency_id; } + if(isset($input['exchange_rate']) && $input['exchange_rate'] == 0) + $input['exchnage_rate'] = 1; + /* Ensure the project is related */ if (array_key_exists('project_id', $input) && isset($input['project_id'])) { $project = Project::withTrashed()->where('id', $input['project_id'])->company()->first(); diff --git a/app/Services/Chart/ChartQueries.php b/app/Services/Chart/ChartQueries.php index df345e348f..82e7270c5d 100644 --- a/app/Services/Chart/ChartQueries.php +++ b/app/Services/Chart/ChartQueries.php @@ -46,7 +46,7 @@ trait ChartQueries SUM( CASE WHEN expenses.currency_id = :company_currency THEN amount - ELSE expenses.amount * expenses.exchange_rate + ELSE expenses.amount * COALESCE(NULLIF(expenses.exchange_rate, 0), 1) END ) AS amount FROM expenses @@ -67,7 +67,7 @@ trait ChartQueries SUM( CASE WHEN expenses.currency_id = :company_currency THEN amount - ELSE expenses.amount * expenses.exchange_rate + ELSE expenses.amount * COALESCE(NULLIF(expenses.exchange_rate, 0), 1) END ) AS total, expenses.date @@ -142,7 +142,7 @@ trait ChartQueries $user_filter = $this->is_admin ? '' : 'AND payments.user_id = '.$this->user->id; return DB::select(" - SELECT sum((payments.amount - payments.refunded) / payments.exchange_rate) as amount, + SELECT sum((payments.amount - payments.refunded) / COALESCE(NULLIF(payments.exchange_rate, 0), 1)) as amount, IFNULL(payments.currency_id, :company_currency) as currency_id FROM payments WHERE payments.company_id = :company_id @@ -166,7 +166,7 @@ trait ChartQueries return DB::select(" SELECT - sum((payments.amount - payments.refunded) * payments.exchange_rate) as total, + sum((payments.amount - payments.refunded) * COALESCE(NULLIF(payments.exchange_rate, 0), 1)) as total, payments.date FROM payments WHERE payments.company_id = :company_id @@ -243,7 +243,7 @@ trait ChartQueries return DB::select(" SELECT - sum(invoices.balance / invoices.exchange_rate) as amount, + SUM(invoices.balance / COALESCE(NULLIF(invoices.exchange_rate, 0), 1)) as amount, COUNT(invoices.id) as outstanding_count FROM clients JOIN invoices @@ -268,7 +268,7 @@ trait ChartQueries return DB::select(" SELECT - sum((payments.amount - payments.refunded) * payments.exchange_rate) as paid_to_date + sum((payments.amount - payments.refunded) * COALESCE(NULLIF(payments.exchange_rate, 0), 1)) as paid_to_date FROM payments JOIN clients ON payments.client_id=clients.id @@ -311,7 +311,7 @@ trait ChartQueries return DB::select(" SELECT - SUM(invoices.amount / invoices.exchange_rate) as invoiced_amount + SUM(invoices.amount / COALESCE(NULLIF(invoices.exchange_rate, 0), 1)) as invoiced_amount FROM clients JOIN invoices ON invoices.client_id = clients.id WHERE invoices.status_id IN (2,3,4) @@ -358,7 +358,7 @@ trait ChartQueries return DB::select(" SELECT - SUM(invoices.balance / invoices.exchange_rate) as total, + SUM(invoices.balance / COALESCE(NULLIF(invoices.exchange_rate, 0), 1)) as total, invoices.date FROM clients JOIN invoices @@ -412,7 +412,7 @@ trait ChartQueries return DB::select(" SELECT - SUM(invoices.amount / invoices.exchange_rate) as total, + SUM(invoices.amount / COALESCE(NULLIF(invoices.exchange_rate, 0), 1)) as total, invoices.date FROM clients JOIN invoices diff --git a/app/Services/Report/TaxSummaryReport.php b/app/Services/Report/TaxSummaryReport.php index a7b757aa6e..fd4583d7db 100644 --- a/app/Services/Report/TaxSummaryReport.php +++ b/app/Services/Report/TaxSummaryReport.php @@ -96,6 +96,9 @@ class TaxSummaryReport extends BaseExport $accrual_map = []; $cash_map = []; + $accrual_invoice_map = []; + $cash_invoice_map = []; + foreach($query->cursor() as $invoice) { $calc = $invoice->calc(); @@ -105,12 +108,19 @@ class TaxSummaryReport extends BaseExport //filter into two arrays for accrual + cash foreach($taxes as $tax) { $key = $tax['name']; + $tax_prorata = 0; if(!isset($accrual_map[$key])) { $accrual_map[$key]['tax_amount'] = 0; } $accrual_map[$key]['tax_amount'] += $tax['total']; + $accrual_invoice_map[] = [ + 'number' => ctrans('texts.invoice') . " " . $invoice->number, + 'date' => $this->translateDate($invoice->date, $this->company->date_format(), $this->company->locale()), + 'formatted' => Number::formatMoney($tax['total'], $this->company), + 'tax' => Number::formatValue($tax['total'], $this->company->currency()), + ]; //cash $key = $tax['name']; @@ -123,10 +133,25 @@ class TaxSummaryReport extends BaseExport try { if($invoice->status_id == Invoice::STATUS_PAID) { + $tax_prorata = $tax['total']; $cash_map[$key]['tax_amount'] += $tax['total']; } else { - $cash_map[$key]['tax_amount'] += (($invoice->amount - $invoice->balance) / $invoice->balance) * $tax['total'] ?? 0; + + $paid_amount = $invoice->amount - $invoice->balance; + $payment_ratio = $invoice->amount > 0 ? $paid_amount / $invoice->amount : 0; + $tax_prorata = round($payment_ratio * ($tax['total'] ?? 0),2); + + $cash_map[$key]['tax_amount'] += $tax_prorata; } + + $cash_invoice_map[] = [ + 'number' => ctrans('texts.invoice') . " " . $invoice->number, + 'date' => $this->translateDate($invoice->date, $this->company->date_format(), $this->company->locale()), + 'formatted' => Number::formatMoney($tax_prorata, $this->company), + 'tax' => Number::formatValue($tax_prorata, $this->company->currency()), + + ]; + } catch(\DivisionByZeroError $e) { $cash_map[$key]['tax_amount'] += 0; } @@ -136,22 +161,38 @@ class TaxSummaryReport extends BaseExport } $this->csv->insertOne([]); - $this->csv->insertOne([ctrans('texts.cash_vs_accrual')]); + $this->csv->insertOne([ctrans('texts.cash_vs_accrual'), ctrans('texts.date'), ctrans('texts.amount'), ctrans('texts.amount')]); $this->csv->insertOne($this->buildHeader()); foreach($accrual_map as $key => $value) { - $this->csv->insertOne([$key, Number::formatMoney($value['tax_amount'], $this->company)]); + $this->csv->insertOne([$key, Number::formatMoney($value['tax_amount'], $this->company), Number::formatValue($value['tax_amount'], $this->company->currency())]); } $this->csv->insertOne([]); - $this->csv->insertOne([ctrans('texts.cash_accounting')]); + $this->csv->insertOne([ctrans('texts.cash_accounting'), ctrans('texts.date'), ctrans('texts.amount'), ctrans('texts.amount')]); $this->csv->insertOne($this->buildHeader()); foreach($cash_map as $key => $value) { - $this->csv->insertOne([$key, Number::formatMoney($value['tax_amount'], $this->company)]); + $this->csv->insertOne([$key, Number::formatMoney($value['tax_amount'], $this->company), Number::formatValue($value['tax_amount'], $this->company->currency())]); + } + + + $this->csv->insertOne([]); + $this->csv->insertOne([]); + $this->csv->insertOne([ctrans('texts.cash_vs_accrual')]); + + foreach($accrual_invoice_map as $map){ + $this->csv->insertOne($map); } + $this->csv->insertOne([]); + $this->csv->insertOne([]); + $this->csv->insertOne([ctrans('texts.cash_accounting')]); + + foreach($cash_invoice_map as $map){ + $this->csv->insertOne($map); + } return $this->csv->toString();