From a821f7e7510cc21a3ded1fca555cb56cf06b55da Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 26 Jul 2023 19:46:06 +1000 Subject: [PATCH 1/8] Updates for e-invoice to include legal literals and also payments --- app/Models/Payment.php | 91 ------------ .../Invoice/EInvoice/FacturaEInvoice.php | 135 +++++++++++++++++- 2 files changed, 134 insertions(+), 92 deletions(-) diff --git a/app/Models/Payment.php b/app/Models/Payment.php index d32630b89720..4502b389a405 100644 --- a/app/Models/Payment.php +++ b/app/Models/Payment.php @@ -94,42 +94,6 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @method static \Illuminate\Database\Eloquent\Builder|Payment onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|Payment query() * @method static \Illuminate\Database\Eloquent\Builder|BaseModel scope() - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereAmount($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereApplied($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereAssignedUserId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereClientContactId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereClientId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereCompanyGatewayId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereCompanyId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereCurrencyId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereCustomValue1($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereCustomValue2($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereCustomValue3($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereCustomValue4($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereDate($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereExchangeCurrencyId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereExchangeRate($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereGatewayTypeId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereIdempotencyKey($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereInvitationId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereIsDeleted($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereIsManual($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereMeta($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereNumber($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment wherePayerId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment wherePrivateNotes($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereProjectId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereRefunded($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereStatusId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereTransactionId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereTransactionReference($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereTypeId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereUpdatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereUserId($value) - * @method static \Illuminate\Database\Eloquent\Builder|Payment whereVendorId($value) * @method static \Illuminate\Database\Eloquent\Builder|Payment withTrashed() * @method static \Illuminate\Database\Eloquent\Builder|Payment withoutTrashed() * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger @@ -137,61 +101,6 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @property-read \Illuminate\Database\Eloquent\Collection $paymentables - * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger - * @property-read \Illuminate\Database\Eloquent\Collection $credits - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $paymentables - * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger - * @property-read \Illuminate\Database\Eloquent\Collection $credits - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $paymentables - * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger - * @property-read \Illuminate\Database\Eloquent\Collection $credits - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $paymentables - * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger - * @property-read \Illuminate\Database\Eloquent\Collection $credits - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $paymentables - * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger - * @property-read \Illuminate\Database\Eloquent\Collection $credits - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $paymentables - * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger - * @property-read \Illuminate\Database\Eloquent\Collection $credits - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $paymentables - * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger - * @property-read \Illuminate\Database\Eloquent\Collection $credits - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $paymentables - * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger - * @property-read \Illuminate\Database\Eloquent\Collection $credits - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $paymentables - * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger - * @property-read \Illuminate\Database\Eloquent\Collection $credits - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $paymentables - * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger - * @property-read \Illuminate\Database\Eloquent\Collection $credits - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $paymentables - * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger - * @property-read \Illuminate\Database\Eloquent\Collection $credits - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $paymentables * @mixin \Eloquent */ class Payment extends BaseModel diff --git a/app/Services/Invoice/EInvoice/FacturaEInvoice.php b/app/Services/Invoice/EInvoice/FacturaEInvoice.php index 4f995ddafa80..39f4a2f3bc0f 100644 --- a/app/Services/Invoice/EInvoice/FacturaEInvoice.php +++ b/app/Services/Invoice/EInvoice/FacturaEInvoice.php @@ -12,13 +12,15 @@ namespace App\Services\Invoice\EInvoice; use App\Models\Invoice; +use App\Models\PaymentType; use josemmo\Facturae\Facturae; use App\Services\AbstractService; use josemmo\Facturae\FacturaeItem; use josemmo\Facturae\FacturaeParty; +use josemmo\Facturae\FacturaeCentre; +use josemmo\Facturae\FacturaePayment; use Illuminate\Support\Facades\Storage; use josemmo\Facturae\Common\FacturaeSigner; -use josemmo\Facturae\FacturaeCentre; class FacturaEInvoice extends AbstractService { @@ -169,6 +171,9 @@ class FacturaEInvoice extends AbstractService ->buildItems() ->setDiscount() ->setPoNumber() + ->setLegalTerms() + ->setPayments() + ->setBillingPeriod() ->signDocument(); $disk = config('filesystems.default'); @@ -228,6 +233,134 @@ class FacturaEInvoice extends AbstractService return $this; } + private function setLegalTerms(): self + { + $this->fac->addLegalLiteral(substr($this->invoice->public_notes,0,250)); + + return $this; + } + + private function setBillingPeriod(): self + { + if (\Carbon\Carbon::createFromFormat('Y-m-d', $this->invoice->custom_value3)->format('Y-m-d') === $this->invoice->custom_value3 && + \Carbon\Carbon::createFromFormat('Y-m-d', $this->invoice->custom_value4)->format('Y-m-d') === $this->invoice->custom_value4 + ){ + $this->fac->setBillingPeriod(\Carbon\Carbon::parse($this->invoice->custom_value3)->format('Y-m-d'), \Carbon\Carbon::parse($this->invoice->custom_value4)->format('Y-m-d')); + } + + return $this; + } + + private function setPayments(): self + { + $this->invoice->payments()->each(function ($payment){ + + $payment_data = [ + "dueDate" => \Carbon\Carbon::parse($payment->date)->format('Y-m-d'), + "amount" => $payment->pivot->amount, + ]; + + $data = array_merge($this->resolvePaymentMethod($payment), $payment_data); + + $this->fac->addPayment(new FacturaePayment($data)); + + }); + + return $this; + } + + /** + * + * FacturaePayment::TYPE_CASH Cash + * FacturaePayment::TYPE_DEBIT Domiciled receipt + * FacturaePayment::TYPE_RECEIPT Receipt + * FacturaePayment::TYPE_TRANSFER Transfer + * FacturaePayment::TYPE_ACCEPTED_BILL_OF_EXCHANGE Letter Accepted + * FacturaePayment::TYPE_DOCUMENTARY_CREDIT Letter of credit + * FacturaePayment::TYPE_CONTRACT_AWARD contract award + * FacturaePayment::TYPE_BILL_OF_EXCHANGE Bill of exchange + * FacturaePayment::TYPE_TRANSFERABLE_IOU I will pay to order + * FacturaePayment::TYPE_IOU I Will Pay Not To Order + * FacturaePayment::TYPE_CHEQUE Check + * FacturaePayment::TYPE_REIMBURSEMENT Replacement + * FacturaePayment::TYPE_SPECIAL specials + * FacturaePayment::TYPE_SETOFF Compensation + * FacturaePayment::TYPE_POSTGIRO Money order + * FacturaePayment::TYPE_CERTIFIED_CHEQUE conformed check + * FacturaePayment::TYPE_BANKERS_DRAFT Bank check + * FacturaePayment::TYPE_CASH_ON_DELIVERY Cash on delivery + * FacturaePayment::TYPE_CARD Payment by card + * + * @param \App\Models\Payment $payment + * @return array + */ + private function resolvePaymentMethod(\App\Models\Payment $payment): array + { + $data = []; + + match($payment->type_id){ + PaymentType::BANK_TRANSFER => $method = FacturaePayment::TYPE_TRANSFER , + PaymentType::CASH => $method = FacturaePayment::TYPE_CASH , + PaymentType::ACH => $method = FacturaePayment::TYPE_TRANSFER , + PaymentType::VISA => $method = FacturaePayment::TYPE_CARD , + PaymentType::MASTERCARD => $method = FacturaePayment::TYPE_CARD , + PaymentType::AMERICAN_EXPRESS => $method = FacturaePayment::TYPE_CARD , + PaymentType::DISCOVER => $method = FacturaePayment::TYPE_CARD , + PaymentType::DINERS => $method = FacturaePayment::TYPE_CARD , + PaymentType::EUROCARD => $method = FacturaePayment::TYPE_CARD , + PaymentType::NOVA => $method = FacturaePayment::TYPE_CARD , + PaymentType::CREDIT_CARD_OTHER => $method = FacturaePayment::TYPE_CARD , + PaymentType::PAYPAL => $method = FacturaePayment::TYPE_CARD , + PaymentType::CHECK => $method = FacturaePayment::TYPE_CHEQUE , + PaymentType::CARTE_BLANCHE => $method = FacturaePayment::TYPE_CARD , + PaymentType::UNIONPAY => $method = FacturaePayment::TYPE_CARD , + PaymentType::JCB => $method = FacturaePayment::TYPE_CARD , + PaymentType::LASER => $method = FacturaePayment::TYPE_CARD , + PaymentType::MAESTRO => $method = FacturaePayment::TYPE_CARD , + PaymentType::SOLO => $method = FacturaePayment::TYPE_CARD , + PaymentType::SWITCH => $method = FacturaePayment::TYPE_CARD , + PaymentType::VENMO => $method = FacturaePayment::TYPE_CARD , + PaymentType::ALIPAY => $method = FacturaePayment::TYPE_CARD , + PaymentType::SOFORT => $method = FacturaePayment::TYPE_TRANSFER, + PaymentType::SEPA => $method = FacturaePayment::TYPE_TRANSFER, + PaymentType::GOCARDLESS => $method = FacturaePayment::TYPE_SPECIAL , + PaymentType::CRYPTO => $method = FacturaePayment::TYPE_SPECIAL , + PaymentType::CREDIT => $method = FacturaePayment::TYPE_DOCUMENTARY_CREDIT , + PaymentType::ZELLE => $method = FacturaePayment::TYPE_SPECIAL , + PaymentType::MOLLIE_BANK_TRANSFER => $method = FacturaePayment::TYPE_TRANSFER , + PaymentType::KBC => $method = FacturaePayment::TYPE_SPECIAL , + PaymentType::BANCONTACT => $method = FacturaePayment::TYPE_SPECIAL , + PaymentType::IDEAL => $method = FacturaePayment::TYPE_SPECIAL , + PaymentType::HOSTED_PAGE => $method = FacturaePayment::TYPE_SPECIAL , + PaymentType::GIROPAY => $method = FacturaePayment::TYPE_SPECIAL , + PaymentType::PRZELEWY24 => $method = FacturaePayment::TYPE_SPECIAL , + PaymentType::EPS => $method = FacturaePayment::TYPE_SPECIAL , + PaymentType::DIRECT_DEBIT => $method = FacturaePayment::TYPE_SPECIAL , + PaymentType::BECS => $method = FacturaePayment::TYPE_SPECIAL , + PaymentType::ACSS => $method = FacturaePayment::TYPE_SPECIAL , + PaymentType::INSTANT_BANK_PAY => $method = FacturaePayment::TYPE_SPECIAL , + PaymentType::FPX => $method = FacturaePayment::TYPE_SPECIAL , + PaymentType::KLARNA => $method = FacturaePayment::TYPE_SPECIAL , + PaymentType::Interac_E_Transfer => $method = FacturaePayment::TYPE_TRANSFER , + PaymentType::BACS => $method = FacturaePayment::TYPE_SPECIAL , + PaymentType::STRIPE_BANK_TRANSFER => $method = FacturaePayment::TYPE_TRANSFER , + PaymentType::CASH_APP => $method = FacturaePayment::TYPE_SPECIAL , + default => $method = FacturaePayment::TYPE_CARD , + }; + + $data['method'] = $method; + + if($method == FacturaePayment::TYPE_TRANSFER) + { + $data['iban'] = $payment->custom_value1; + $data['bic'] = $payment->custom_value2; + } + + return $data; + + + } + private function buildItems(): self { From f16eb06d52fce3240f1311f8f862f007848fd867 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 26 Jul 2023 20:24:03 +1000 Subject: [PATCH 2/8] Fixes for e-invoicing --- app/Services/Invoice/EInvoice/FacturaEInvoice.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/app/Services/Invoice/EInvoice/FacturaEInvoice.php b/app/Services/Invoice/EInvoice/FacturaEInvoice.php index 39f4a2f3bc0f..df92882a0a42 100644 --- a/app/Services/Invoice/EInvoice/FacturaEInvoice.php +++ b/app/Services/Invoice/EInvoice/FacturaEInvoice.php @@ -242,10 +242,15 @@ class FacturaEInvoice extends AbstractService private function setBillingPeriod(): self { - if (\Carbon\Carbon::createFromFormat('Y-m-d', $this->invoice->custom_value3)->format('Y-m-d') === $this->invoice->custom_value3 && - \Carbon\Carbon::createFromFormat('Y-m-d', $this->invoice->custom_value4)->format('Y-m-d') === $this->invoice->custom_value4 - ){ - $this->fac->setBillingPeriod(\Carbon\Carbon::parse($this->invoice->custom_value3)->format('Y-m-d'), \Carbon\Carbon::parse($this->invoice->custom_value4)->format('Y-m-d')); + try { + if (\Carbon\Carbon::createFromFormat('Y-m-d', $this->invoice->custom_value3)->format('Y-m-d') === $this->invoice->custom_value3 && + \Carbon\Carbon::createFromFormat('Y-m-d', $this->invoice->custom_value4)->format('Y-m-d') === $this->invoice->custom_value4 + ) { + $this->fac->setBillingPeriod(\Carbon\Carbon::parse($this->invoice->custom_value3)->format('Y-m-d'), \Carbon\Carbon::parse($this->invoice->custom_value4)->format('Y-m-d')); + } + } + catch(\Exception $e) { + nlog($e->getMessage()); } return $this; From 395c71ca11c5d1d33f3723d2d1e01ac7501fa274 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 27 Jul 2023 07:33:09 +1000 Subject: [PATCH 3/8] remove currency formatting of payments from exports --- app/Export/CSV/BaseExport.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/Export/CSV/BaseExport.php b/app/Export/CSV/BaseExport.php index 8576bdfa8337..3270a5a44893 100644 --- a/app/Export/CSV/BaseExport.php +++ b/app/Export/CSV/BaseExport.php @@ -650,17 +650,17 @@ class BaseExport } if($column == 'amount') - return $entity->payments()->exists() ? Number::formatMoney($entity->payments()->sum('paymentables.amount'), $entity->company) : ctrans('texts.unpaid'); + return $entity->payments()->exists() ? $entity->payments()->sum('paymentables.amount') : ctrans('texts.unpaid'); if($column == 'refunded') { - return $entity->payments()->exists() ? Number::formatMoney($entity->payments()->sum('paymentables.refunded'), $entity->company) : 0; + return $entity->payments()->exists() ? $entity->payments()->sum('paymentables.refunded') : 0; } if($column == 'applied') { $refunded = $entity->payments()->sum('paymentables.refunded'); $amount = $entity->payments()->sum('paymentables.amount'); - return $entity->payments()->exists() ? Number::formatMoney(($amount - $refunded), $entity->company) : 0; + return $entity->payments()->exists() ? ($amount - $refunded) : 0; } $payment = $entity->payments()->first(); From 3229ae871a172a56179c32565a6d339da61a17ef Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 27 Jul 2023 09:02:13 +1000 Subject: [PATCH 4/8] Fixes for recurring invoices --- .../StoreRecurringInvoiceRequest.php | 4 + app/Models/RecurringInvoice.php | 117 ------------------ tests/Feature/RecurringInvoiceTest.php | 45 +++++++ 3 files changed, 49 insertions(+), 117 deletions(-) diff --git a/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php index 6a941b1fa394..307ab9cffe5f 100644 --- a/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php +++ b/app/Http/Requests/RecurringInvoice/StoreRecurringInvoiceRequest.php @@ -80,6 +80,10 @@ class StoreRecurringInvoiceRequest extends Request $input['due_date_days'] = 'terms'; } + if(!isset($input['next_send_date']) || $input['next_send_date'] == '') { + $input['next_send_date'] = now()->format('Y-m-d'); + } + if (array_key_exists('next_send_date', $input) && is_string($input['next_send_date'])) { $input['next_send_date_client'] = $input['next_send_date']; } diff --git a/app/Models/RecurringInvoice.php b/app/Models/RecurringInvoice.php index eab645792ac8..a41f58629fb6 100644 --- a/app/Models/RecurringInvoice.php +++ b/app/Models/RecurringInvoice.php @@ -114,67 +114,6 @@ use Laracasts\Presenter\PresentableTrait; * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice onlyTrashed() * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice query() * @method static \Illuminate\Database\Eloquent\Builder|BaseModel scope() - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereAmount($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereAssignedUserId($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereAutoBill($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereAutoBillEnabled($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereBackup($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereBalance($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereClientId($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereCompanyId($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereCustomSurcharge1($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereCustomSurcharge2($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereCustomSurcharge3($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereCustomSurcharge4($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereCustomSurchargeTax1($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereCustomSurchargeTax2($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereCustomSurchargeTax3($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereCustomSurchargeTax4($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereCustomValue1($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereCustomValue2($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereCustomValue3($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereCustomValue4($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereDate($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereDeletedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereDesignId($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereDiscount($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereDueDate($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereDueDateDays($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereExchangeRate($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereFooter($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereFrequencyId($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereIsAmountDiscount($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereIsDeleted($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereLastSentDate($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereLastViewed($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereLineItems($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereNextSendDate($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereNextSendDateClient($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereNumber($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice wherePaidToDate($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice wherePartial($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice wherePartialDueDate($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice wherePoNumber($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice wherePrivateNotes($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereProjectId($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice wherePublicNotes($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereRemainingCycles($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereStatusId($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereSubscriptionId($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereTaxName1($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereTaxName2($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereTaxName3($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereTaxRate1($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereTaxRate2($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereTaxRate3($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereTerms($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereTotalTaxes($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereUpdatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereUserId($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereUsesInclusiveTaxes($value) - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereVendorId($value) * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice withTrashed() * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice withoutTrashed() * @property-read \Illuminate\Database\Eloquent\Collection $activities @@ -183,62 +122,6 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @property bool $is_proforma - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $history - * @property-read \Illuminate\Database\Eloquent\Collection $invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @method static \Illuminate\Database\Eloquent\Builder|RecurringInvoice whereIsProforma($value) - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $history - * @property-read \Illuminate\Database\Eloquent\Collection $invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $history - * @property-read \Illuminate\Database\Eloquent\Collection $invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $history - * @property-read \Illuminate\Database\Eloquent\Collection $invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $history - * @property-read \Illuminate\Database\Eloquent\Collection $invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $history - * @property-read \Illuminate\Database\Eloquent\Collection $invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $history - * @property-read \Illuminate\Database\Eloquent\Collection $invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $history - * @property-read \Illuminate\Database\Eloquent\Collection $invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $history - * @property-read \Illuminate\Database\Eloquent\Collection $invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $history - * @property-read \Illuminate\Database\Eloquent\Collection $invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoices - * @property-read \Illuminate\Database\Eloquent\Collection $activities - * @property-read \Illuminate\Database\Eloquent\Collection $documents - * @property-read \Illuminate\Database\Eloquent\Collection $history - * @property-read \Illuminate\Database\Eloquent\Collection $invitations - * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @mixin \Eloquent */ class RecurringInvoice extends BaseModel diff --git a/tests/Feature/RecurringInvoiceTest.php b/tests/Feature/RecurringInvoiceTest.php index e13b0e7026e6..def58d0e50cc 100644 --- a/tests/Feature/RecurringInvoiceTest.php +++ b/tests/Feature/RecurringInvoiceTest.php @@ -40,6 +40,8 @@ class RecurringInvoiceTest extends TestCase use DatabaseTransactions; use MockAccountData; + public $faker; + protected function setUp() :void { parent::setUp(); @@ -58,6 +60,49 @@ class RecurringInvoiceTest extends TestCase } + public function testNextSendDateCatch() + { + $line_items = []; + + $item = InvoiceItemFactory::create(); + $item->quantity = 1; + $item->cost = 10; + $item->task_id = $this->encodePrimaryKey($this->task->id); + $item->expense_id = $this->encodePrimaryKey($this->expense->id); + $item->notes = "Hello this is the month of :MONTH"; + + $line_items[] = $item; + + + $data = [ + 'frequency_id' => 1, + 'status_id' => 2, + 'discount' => 0, + 'is_amount_discount' => 1, + 'po_number' => '3434343', + 'public_notes' => 'notes', + 'is_deleted' => 0, + 'custom_value1' => 0, + 'custom_value2' => 0, + 'custom_value3' => 0, + 'custom_value4' => 0, + 'status' => 1, + 'client_id' => $this->encodePrimaryKey($this->client->id), + 'line_items' => $line_items, + 'remaining_cycles' => -1, + ]; + + $response = $this->withHeaders([ + 'X-API-SECRET' => config('ninja.api_secret'), + 'X-API-TOKEN' => $this->token, + ])->post('/api/v1/recurring_invoices/', $data) + ->assertStatus(200); + + $arr = $response->json(); + + $this->assertEquals(now()->startOfDay(), $arr['data']['next_send_date']); + + } public function testBulkIncreasePriceWithJob() { From 2417e2aecde030c3fcc2cc8ff98ead607be03410 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 27 Jul 2023 11:14:59 +1000 Subject: [PATCH 5/8] Fixes for permissions to view tax rates --- app/Http/Controllers/BaseController.php | 6 +- app/Http/Controllers/ExpenseController.php | 4 +- app/Http/Controllers/TwilioController.php | 56 ++++++++++++------- .../UpdateRecurringInvoiceRequest.php | 4 ++ app/Models/User.php | 4 +- app/Services/Payment/DeletePayment.php | 6 ++ 6 files changed, 54 insertions(+), 26 deletions(-) diff --git a/app/Http/Controllers/BaseController.php b/app/Http/Controllers/BaseController.php index b3fc93c0938d..156e5384ced9 100644 --- a/app/Http/Controllers/BaseController.php +++ b/app/Http/Controllers/BaseController.php @@ -951,6 +951,9 @@ class BaseController extends Controller if ($this->entity_type == BankIntegration::class && !$user->isSuperUser() && $user->hasIntersectPermissions(['create_bank_transaction','edit_bank_transaction','view_bank_transaction'])) { $query->exclude(["balance"]); } //allows us to selective display bank integrations back to the user if they can view / create bank transactions but without the bank balance being present in the response + elseif($this->entity_type == TaxRate::class && $user->hasIntersectPermissions(['create_invoice','edit_invoice','create_quote','edit_quote','create_purchase_order','edit_purchase_order'])){ + // need to show tax rates if the user has the ability to create documents. + } else { $query->where('user_id', '=', $user->id); } @@ -980,9 +983,6 @@ class BaseController extends Controller $resource = new Collection($query, $transformer, $this->entity_type); $resource->setPaginator(new IlluminatePaginatorAdapter($paginator)); } - // else { - // $resource = new Collection($query, $transformer, $this->entity_type); - // } return $this->response($this->manager->createData($resource)->toArray()); } diff --git a/app/Http/Controllers/ExpenseController.php b/app/Http/Controllers/ExpenseController.php index 626399415b25..476f206f5ad1 100644 --- a/app/Http/Controllers/ExpenseController.php +++ b/app/Http/Controllers/ExpenseController.php @@ -49,7 +49,7 @@ class ExpenseController extends BaseController protected $entity_transformer = ExpenseTransformer::class; /** - * @var ExpensRepository + * @var ExpenseRepository */ protected $expense_repo; @@ -72,7 +72,7 @@ class ExpenseController extends BaseController * summary="Gets a list of expenses", * description="Lists expenses, search and filters allow fine grained lists to be generated. - Query parameters can be added to performed more fine grained filtering of the expenses, these are handled by the ExpenseFilters class which defines the methods available", + * Query parameters can be added to performed more fine grained filtering of the expenses, these are handled by the ExpenseFilters class which defines the methods available", * @OA\Parameter(ref="#/components/parameters/X-API-TOKEN"), * @OA\Parameter(ref="#/components/parameters/X-Requested-With"), * @OA\Parameter(ref="#/components/parameters/include"), diff --git a/app/Http/Controllers/TwilioController.php b/app/Http/Controllers/TwilioController.php index 465e1b989830..1f26720773ca 100644 --- a/app/Http/Controllers/TwilioController.php +++ b/app/Http/Controllers/TwilioController.php @@ -11,13 +11,13 @@ namespace App\Http\Controllers; +use App\Models\User; +use Twilio\Rest\Client; +use App\Libraries\MultiDB; use App\Http\Requests\Twilio\Confirm2faRequest; use App\Http\Requests\Twilio\ConfirmSmsRequest; use App\Http\Requests\Twilio\Generate2faRequest; use App\Http\Requests\Twilio\GenerateSmsRequest; -use App\Libraries\MultiDB; -use App\Models\User; -use Twilio\Rest\Client; class TwilioController extends BaseController { @@ -29,11 +29,14 @@ class TwilioController extends BaseController /** * Display a listing of the resource. * - * @return void + * @return \Illuminate\Http\JsonResponse; */ public function generate(GenerateSmsRequest $request) { - $account = auth()->user()->company()->account; + /** @var \App\Models\User $user */ + $user = auth()->user(); + + $account = $user->company()->account; if (MultiDB::hasPhoneNumber($request->phone)) { return response()->json(['message' => 'This phone number has already been verified with another account'], 400); @@ -65,11 +68,14 @@ class TwilioController extends BaseController /** * Show the form for creating a new resource. * - * @return void + * @return \Illuminate\Http\JsonResponse; */ public function confirm(ConfirmSmsRequest $request) { - $account = auth()->user()->company()->account; + /** @var \App\Models\User $user */ + $user = auth()->user(); + + $account = $user->company()->account; $sid = config('ninja.twilio_account_sid'); $token = config('ninja.twilio_auth_token'); @@ -90,8 +96,9 @@ class TwilioController extends BaseController $account->account_sms_verified = true; $account->save(); - //on confirmation we set the users phone number. + /** @var \App\Models\User $user */ $user = auth()->user(); + $user->phone = $account->account_sms_verification_number; $user->verified_phone_number = true; $user->save(); @@ -102,7 +109,12 @@ class TwilioController extends BaseController return response()->json(['message' => 'SMS not verified'], 400); } - + + /** + * generate2faResetCode + * + * @return \Illuminate\Http\JsonResponse; + */ public function generate2faResetCode(Generate2faRequest $request) { $user = User::where('email', $request->email)->first(); @@ -131,7 +143,13 @@ class TwilioController extends BaseController return response()->json(['message' => 'Code sent.'], 200); } - + + /** + * confirm2faResetCode + * + * @param Confirm2faRequest $request + * @return \Illuminate\Http\JsonResponse; + */ public function confirm2faResetCode(Confirm2faRequest $request) { $user = User::where('email', $request->email)->first(); @@ -171,16 +189,16 @@ class TwilioController extends BaseController return response()->json(['message' => 'SMS not verified.'], 400); } - public function validatePhoneNumber() - { - $sid = config('ninja.twilio_account_sid'); - $token = config('ninja.twilio_auth_token'); + // public function validatePhoneNumber() + // { + // $sid = config('ninja.twilio_account_sid'); + // $token = config('ninja.twilio_auth_token'); - $twilio = new Client($sid, $token); + // $twilio = new Client($sid, $token); - $phone_number = $twilio->lookups->v1->phoneNumbers("0417918829") - ->fetch(["countryCode" => "AU"]); + // $phone_number = $twilio->lookups->v1->phoneNumbers("0417918829") + // ->fetch(["countryCode" => "AU"]); - print($phone_number); - } + // print($phone_number); + // } } diff --git a/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php b/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php index a7d54fd235e2..8b9795a78cbf 100644 --- a/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php +++ b/app/Http/Requests/RecurringInvoice/UpdateRecurringInvoiceRequest.php @@ -74,6 +74,10 @@ class UpdateRecurringInvoiceRequest extends Request $input['due_date_days'] = 'terms'; } + if(!isset($input['next_send_date']) || $input['next_send_date'] == '') { + $input['next_send_date'] = now()->format('Y-m-d'); + } + if (array_key_exists('next_send_date', $input) && is_string($input['next_send_date'])) { $input['next_send_date_client'] = $input['next_send_date']; } diff --git a/app/Models/User.php b/app/Models/User.php index 814d501052a1..a765ddef6d62 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -57,7 +57,7 @@ use Illuminate\Foundation\Auth\User as Authenticatable; * @property int|null $avatar_width * @property int|null $avatar_height * @property int|null $avatar_size - * @property int $is_deleted + * @property bool $is_deleted * @property string|null $last_login * @property string|null $signature * @property string $password @@ -74,7 +74,7 @@ use Illuminate\Foundation\Auth\User as Authenticatable; * @property int $has_password * @property Carbon|null $oauth_user_token_expiry * @property string|null $sms_verification_code - * @property int $verified_phone_number + * @property bool $verified_phone_number * @property-read \App\Models\Account $account * @property-read \App\Models\Company $company * @property-read mixed $hashed_id diff --git a/app/Services/Payment/DeletePayment.php b/app/Services/Payment/DeletePayment.php index d1fd931b1cb2..38002a9057be 100644 --- a/app/Services/Payment/DeletePayment.php +++ b/app/Services/Payment/DeletePayment.php @@ -14,6 +14,7 @@ namespace App\Services\Payment; use App\Models\Credit; use App\Models\Invoice; use App\Models\Payment; +use App\Models\BankTransaction; use Illuminate\Contracts\Container\BindingResolutionException; class DeletePayment @@ -56,6 +57,11 @@ class DeletePayment $this->payment->is_deleted = true; $this->payment->delete(); + // BankTransaction::where('payment_id', $this->payment->id)->cursor()->each(function ($bt){ + // $bt->payment_id = null; + // $bt->save(); + // }); + return $this; } From 0e152fff2618740dd5ab57927909dda86de6a8f6 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 27 Jul 2023 11:25:05 +1000 Subject: [PATCH 6/8] Fixes for tess --- app/Export/CSV/BaseExport.php | 14 +++++++------- tests/Feature/Export/ReportCsvGenerationTest.php | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/Export/CSV/BaseExport.php b/app/Export/CSV/BaseExport.php index 3270a5a44893..483872efaeee 100644 --- a/app/Export/CSV/BaseExport.php +++ b/app/Export/CSV/BaseExport.php @@ -454,7 +454,7 @@ class BaseExport private function resolveTaskKey($column, $entity, $transformer) { - nlog("searching for {$column}"); + // nlog("searching for {$column}"); $transformed_entity = $transformer->transform($entity); @@ -499,7 +499,7 @@ class BaseExport if(array_key_exists($column, $transformed_entity)) return $transformed_entity[$column]; - nlog("export: Could not resolve vendor key: {$column}"); + // nlog("export: Could not resolve vendor key: {$column}"); return ''; @@ -547,7 +547,7 @@ class BaseExport if(array_key_exists($column, $transformed_client)) return $transformed_client[$column]; - nlog("export: Could not resolve client key: {$column}"); + // nlog("export: Could not resolve client key: {$column}"); return ''; @@ -555,7 +555,7 @@ class BaseExport private function resolvePurchaseOrderKey($column, $entity, $transformer) { - nlog("searching for {$column}"); + // nlog("searching for {$column}"); $transformed_entity = $transformer->transform($entity); @@ -567,7 +567,7 @@ class BaseExport private function resolveQuoteKey($column, $entity, $transformer) { - nlog("searching for {$column}"); + // nlog("searching for {$column}"); $transformed_entity = $transformer->transform($entity); @@ -581,7 +581,7 @@ class BaseExport private function resolveInvoiceKey($column, $entity, $transformer) { - nlog("searching for {$column}"); + // nlog("searching for {$column}"); $transformed_invoice = false; if($transformer instanceof PaymentTransformer) { @@ -643,7 +643,7 @@ class BaseExport return $transformed_payment[$column]; } - nlog("export: Could not resolve payment key: {$column}"); + // nlog("export: Could not resolve payment key: {$column}"); return ''; diff --git a/tests/Feature/Export/ReportCsvGenerationTest.php b/tests/Feature/Export/ReportCsvGenerationTest.php index 53a9c3924255..4ae21985a610 100644 --- a/tests/Feature/Export/ReportCsvGenerationTest.php +++ b/tests/Feature/Export/ReportCsvGenerationTest.php @@ -1226,7 +1226,7 @@ class ReportCsvGenerationTest extends TestCase $this->assertEquals('bob', $this->getFirstValueByColumn($csv, 'Client Name')); $this->assertEquals('12345', $this->getFirstValueByColumn($csv, 'Invoice Invoice Number')); - $this->assertEquals('$100.00', $this->getFirstValueByColumn($csv, 'Payment Amount')); + $this->assertEquals(100, $this->getFirstValueByColumn($csv, 'Payment Amount')); $this->assertEquals(now()->addSeconds($this->company->timezone()->utc_offset)->format('Y-m-d'), $this->getFirstValueByColumn($csv, 'Payment Date')); From aac1cab4485983beb3d7cc7597b8195d2e01ce6b Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 27 Jul 2023 17:33:55 +1000 Subject: [PATCH 7/8] Fixes for timezone correction --- app/Jobs/Util/ReminderJob.php | 4 +- app/Utils/Traits/MakesReminders.php | 24 +++++++---- tests/Feature/ReminderTest.php | 62 +++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 9 deletions(-) diff --git a/app/Jobs/Util/ReminderJob.php b/app/Jobs/Util/ReminderJob.php index 2f80eeb6c628..3a2d00919cc5 100644 --- a/app/Jobs/Util/ReminderJob.php +++ b/app/Jobs/Util/ReminderJob.php @@ -125,7 +125,7 @@ class ReminderJob implements ShouldQueue } $reminder_template = $invoice->calculateTemplate('invoice'); - // nlog("reminder template = {$reminder_template}"); + nlog("reminder template = {$reminder_template}"); $invoice->service()->touchReminder($reminder_template)->save(); $fees = $this->calcLateFee($invoice, $reminder_template); @@ -139,7 +139,7 @@ class ReminderJob implements ShouldQueue if ($reminder_template == 'endless_reminder') { $enabled_reminder = 'enable_reminder_endless'; } - + if (in_array($reminder_template, ['reminder1', 'reminder2', 'reminder3', 'reminder_endless', 'endless_reminder']) && $invoice->client->getSetting($enabled_reminder) && $invoice->client->getSetting('send_reminders') && diff --git a/app/Utils/Traits/MakesReminders.php b/app/Utils/Traits/MakesReminders.php index 2d9f8b80b607..d886ffaacbb8 100644 --- a/app/Utils/Traits/MakesReminders.php +++ b/app/Utils/Traits/MakesReminders.php @@ -19,21 +19,31 @@ use Illuminate\Support\Carbon; */ trait MakesReminders { + /** + * + * @param string $schedule_reminder + * @param string $num_days_reminder + * @return ?bool + */ public function inReminderWindow($schedule_reminder, $num_days_reminder) { + + $offset = $this->client->timezone_offset(); + + // nlog($schedule_reminder. " ". $num_days_reminder); + // nlog("date = " . Carbon::parse($this->date)->addDays($num_days_reminder)->startOfDay()->addSeconds($offset)); + // nlog("due date = " . Carbon::parse($this->due_date)->addDays($num_days_reminder)->startOfDay()->addSeconds($offset)); + // nlog("now = " . Carbon::now()->startOfDay()->format('Y-m-d H:i:s')); + switch ($schedule_reminder) { case 'after_invoice_date': - return Carbon::parse($this->date)->addDays($num_days_reminder)->startOfDay()->eq(Carbon::now()->startOfDay()); - break; + return Carbon::parse($this->date)->addDays($num_days_reminder)->startOfDay()->addSeconds($offset)->eq(Carbon::now()); case 'before_due_date': - return Carbon::parse($this->due_date)->subDays($num_days_reminder)->startOfDay()->eq(Carbon::now()->startOfDay()); - break; + return Carbon::parse($this->due_date)->subDays($num_days_reminder)->startOfDay()->addSeconds($offset)->eq(Carbon::now()); case 'after_due_date': - return Carbon::parse($this->due_date)->addDays($num_days_reminder)->startOfDay()->eq(Carbon::now()->startOfDay()); - break; + return Carbon::parse($this->due_date)->addDays($num_days_reminder)->startOfDay()->addSeconds($offset)->eq(Carbon::now()); default: return null; - break; } } diff --git a/tests/Feature/ReminderTest.php b/tests/Feature/ReminderTest.php index 8be2e0952e6c..5812e955db47 100644 --- a/tests/Feature/ReminderTest.php +++ b/tests/Feature/ReminderTest.php @@ -50,6 +50,68 @@ class ReminderTest extends TestCase $this->withoutExceptionHandling(); } + public function testForReminderFiringCorrectly() + { + + $this->invoice->next_send_date = null; + $this->invoice->date = now()->format('Y-m-d'); + $this->invoice->last_sent_date = now(); + $this->invoice->due_date = Carbon::now()->addDays(5)->format('Y-m-d'); + $this->invoice->reminder_last_sent = null; + $this->invoice->save(); + + $settings = $this->company->settings; + $settings->enable_reminder1 = true; + $settings->schedule_reminder1 = 'after_invoice_date'; + $settings->num_days_reminder1 = 2; + $settings->enable_reminder2 = false; + $settings->schedule_reminder2 = ''; + $settings->num_days_reminder2 = 0; + $settings->enable_reminder3 = false; + $settings->schedule_reminder3 = ''; + $settings->num_days_reminder3 = 0; + $settings->timezone_id = '109'; + $settings->entity_send_time = 6; + $settings->endless_reminder_frequency_id = ''; + $settings->enable_reminder_endless = false; + + $this->client->company->settings = $settings; + $this->client->push(); + + $client_settings = $settings; + $client_settings->timezone_id = '5'; + $client_settings->entity_send_time = 8; + + $this->invoice->client->settings = $client_settings; + $this->invoice->push(); + + $this->invoice = $this->invoice->service()->markSent()->save(); + $this->invoice->service()->setReminder($client_settings)->save(); + + $this->invoice = $this->invoice->fresh(); + + //due to UTC server time, we actually send the "day before" + $this->assertEquals(now()->addDays(1)->format('Y-m-d'), Carbon::parse($this->invoice->next_send_date)->format('Y-m-d')); + + $this->travelTo(now()->startOfDay()); + + for($x=0; $x<46; $x++) { + + // nlog("traveller {$x} ".now()->format('Y-m-d h:i:s')); + (new ReminderJob())->handle(); + $this->invoice = $this->invoice->fresh(); + $this->assertNull($this->invoice->reminder1_sent); + $this->assertNull($this->invoice->reminder_last_sent); + + $this->travelTo(now()->addHours(1)); + } + + // nlog("traveller ".now()->format('Y-m-d')); + (new ReminderJob())->handle(); + $this->invoice = $this->invoice->fresh(); + $this->assertNotNull($this->invoice->reminder1_sent); + + } public function testForSingleEndlessReminder() { From 80e1dd4f1f958b3ac6713c7077042969517ef9fd Mon Sep 17 00:00:00 2001 From: David Bomba Date: Thu, 27 Jul 2023 17:35:47 +1000 Subject: [PATCH 8/8] Fixes for timezone corrections for reminders --- tests/Feature/ReminderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Feature/ReminderTest.php b/tests/Feature/ReminderTest.php index 5812e955db47..c55c302d8c22 100644 --- a/tests/Feature/ReminderTest.php +++ b/tests/Feature/ReminderTest.php @@ -195,7 +195,7 @@ class ReminderTest extends TestCase nlog($next_send_date->format('Y-m-d h:i:s')); nlog($calculatedReminderDate->format('Y-m-d h:i:s')); - $this->travelTo(now()->addDays(1)); + $this->travelTo($calculatedReminderDate); $reminder_template = $this->invoice->calculateTemplate('invoice');