diff --git a/app/Console/Commands/CreateTestData.php b/app/Console/Commands/CreateTestData.php
index 36aeee130ef4..40d74f98e2b9 100644
--- a/app/Console/Commands/CreateTestData.php
+++ b/app/Console/Commands/CreateTestData.php
@@ -321,11 +321,11 @@ class CreateTestData extends Command
if(rand(0, 1)) {
$payment = PaymentFactory::create($client->company->id, $client->user->id);
- $payment->payment_date = now();
+ $payment->date = now();
$payment->client_id = $client->id;
$payment->amount = $invoice->balance;
$payment->transaction_reference = rand(0,500);
- $payment->payment_type_id = PaymentType::CREDIT_CARD_OTHER;
+ $payment->type_id = PaymentType::CREDIT_CARD_OTHER;
$payment->status_id = Payment::STATUS_COMPLETED;
$payment->save();
diff --git a/app/DataMapper/CompanySettings.php b/app/DataMapper/CompanySettings.php
index 698e2134d7be..db9d4f4e16ae 100644
--- a/app/DataMapper/CompanySettings.php
+++ b/app/DataMapper/CompanySettings.php
@@ -179,6 +179,8 @@ class CompanySettings extends BaseSettings
public $schedule_reminder2 = ''; // (enum: after_invoice_date, before_due_date, after_due_date)
public $schedule_reminder3 = ''; // (enum: after_invoice_date, before_due_date, after_due_date)
+ public $reminder_send_time = 32400; //number of seconds from UTC +0 to send reminders
+
public $late_fee_amount1 = 0;
public $late_fee_amount2 = 0;
public $late_fee_amount3 = 0;
@@ -215,6 +217,7 @@ class CompanySettings extends BaseSettings
public static $casts = [
+ 'reminder_send_time' => 'int',
'email_sending_method' => 'string',
'gmail_sending_user_id' => 'string',
'currency_id' => 'string',
diff --git a/app/DataMapper/EmailTemplateDefaults.php b/app/DataMapper/EmailTemplateDefaults.php
index ca7970b81f26..9cbdafa19aaa 100644
--- a/app/DataMapper/EmailTemplateDefaults.php
+++ b/app/DataMapper/EmailTemplateDefaults.php
@@ -17,7 +17,7 @@ class EmailTemplateDefaults
{
public static function emailInvoiceSubject()
{
- return ctrans('invoice_subject', ['number'=>'$number', 'account'=>'$company']);
+ return ctrans('texts.invoice_subject', ['number'=>'$number', 'account'=>'$company.name']);
//return Parsedown::instance()->line(self::transformText('invoice_subject'));
}
@@ -28,7 +28,7 @@ class EmailTemplateDefaults
public static function emailQuoteSubject()
{
- return ctrans('quote_subject', ['number'=>'$number', 'account'=>'$company']);
+ return ctrans('texts.quote_subject', ['number'=>'$number', 'account'=>'$company.name']);
//return Parsedown::instance()->line(self::transformText('quote_subject'));
}
@@ -51,9 +51,7 @@ class EmailTemplateDefaults
public static function emailReminder1Subject()
{
- return ctrans('reminder_subject', ['invoice'=>'$number', 'account'=>'$company']);
-
- // return Parsedown::instance()->line(self::transformText('reminder_subject'));
+ return ctrans('texts.reminder_subject', ['invoice'=>'$invoice.number', 'account'=>'$company.name']);
}
public static function emailReminder1Template()
@@ -63,7 +61,7 @@ class EmailTemplateDefaults
public static function emailReminder2Subject()
{
- return ctrans('reminder_subject', ['invoice'=>'$number', 'account'=>'$company']);
+ return ctrans('texts.reminder_subject', ['invoice'=>'$invoice.number', 'account'=>'$company.name']);
// return Parsedown::instance()->line(self::transformText('reminder_subject'));
}
@@ -74,7 +72,7 @@ class EmailTemplateDefaults
public static function emailReminder3Subject()
{
- return ctrans('reminder_subject', ['invoice'=>'$number', 'account'=>'$company']);
+ return ctrans('texts.reminder_subject', ['invoice'=>'$invoice.number', 'account'=>'$company.name']);
// return Parsedown::instance()->line(self::transformText('reminder_subject'));
}
@@ -85,7 +83,7 @@ class EmailTemplateDefaults
public static function emailReminderEndlessSubject()
{
- return ctrans('reminder_subject', ['invoice'=>'$number', 'account'=>'$company']);
+ return ctrans('texts.reminder_subject', ['invoice'=>'$invoice.number', 'account'=>'$company.name']);
// return Parsedown::instance()->line(self::transformText('reminder_subject'));
}
diff --git a/app/DataMapper/PaymentTransaction.php b/app/DataMapper/PaymentTransaction.php
index 8097902e3163..358cd0f7b4c4 100644
--- a/app/DataMapper/PaymentTransaction.php
+++ b/app/DataMapper/PaymentTransaction.php
@@ -20,7 +20,7 @@ class PaymentTransaction
public $account_gateway_id;
- public $payment_type_id;
+ public $type_id;
public $status; // prepayment|payment|response|completed
diff --git a/app/Factory/PaymentFactory.php b/app/Factory/PaymentFactory.php
index 42a120a33e2a..d16715541baa 100644
--- a/app/Factory/PaymentFactory.php
+++ b/app/Factory/PaymentFactory.php
@@ -29,10 +29,10 @@ class PaymentFactory
$payment->client_contact_id = null;
$payment->invitation_id = null;
$payment->company_gateway_id = null;
- $payment->payment_type_id = null;
+ $payment->type_id = null;
$payment->is_deleted = false;
$payment->amount = 0;
- $payment->payment_date = Carbon::now()->format('Y-m-d');
+ $payment->date = Carbon::now()->format('Y-m-d');
$payment->transaction_reference = null;
$payment->payer_id = null;
$payment->status_id = Payment::STATUS_PENDING;
diff --git a/app/Filters/PaymentFilters.php b/app/Filters/PaymentFilters.php
index 407e9cb05e99..90d83cd5ef90 100644
--- a/app/Filters/PaymentFilters.php
+++ b/app/Filters/PaymentFilters.php
@@ -37,7 +37,7 @@ class PaymentFilters extends QueryFilters
return $this->builder->where(function ($query) use ($filter) {
$query->where('payments.amount', 'like', '%'.$filter.'%')
- ->orWhere('payments.payment_date', 'like', '%'.$filter.'%')
+ ->orWhere('payments.date', 'like', '%'.$filter.'%')
->orWhere('payments.custom_value1', 'like', '%'.$filter.'%')
->orWhere('payments.custom_value2', 'like' , '%'.$filter.'%')
->orWhere('payments.custom_value3', 'like' , '%'.$filter.'%')
diff --git a/app/Http/Controllers/ClientPortal/PaymentController.php b/app/Http/Controllers/ClientPortal/PaymentController.php
index 9b049f05ff73..191233b5ef27 100644
--- a/app/Http/Controllers/ClientPortal/PaymentController.php
+++ b/app/Http/Controllers/ClientPortal/PaymentController.php
@@ -52,20 +52,20 @@ class PaymentController extends Controller
return DataTables::of($payments)->addColumn('action', function ($payment) {
return ''.ctrans('texts.view').'';
- })->editColumn('payment_type_id', function ($payment) {
+ })->editColumn('type_id', function ($payment) {
return $payment->type->name;
})
->editColumn('status_id', function ($payment){
return Payment::badgeForStatus($payment->status_id);
})
- ->editColumn('payment_date', function ($payment){
- //return $payment->payment_date;
- return $payment->formatDate($payment->payment_date, $payment->client->date_format());
+ ->editColumn('date', function ($payment){
+ //return $payment->date;
+ return $payment->formatDate($payment->date, $payment->client->date_format());
})
->editColumn('amount', function ($payment) {
return Number::formatMoney($payment->amount, $payment->client);
})
- ->rawColumns(['action', 'status_id','payment_type_id'])
+ ->rawColumns(['action', 'status_id','type_id'])
->make(true);
}
diff --git a/app/Http/Controllers/OpenAPI/CompanySettingsSchema.php b/app/Http/Controllers/OpenAPI/CompanySettingsSchema.php
index 9f583355e762..199d456998e8 100644
--- a/app/Http/Controllers/OpenAPI/CompanySettingsSchema.php
+++ b/app/Http/Controllers/OpenAPI/CompanySettingsSchema.php
@@ -37,6 +37,7 @@
* @OA\Property(property="translations", type="object", example="", description="JSON payload of customized translations"),
* @OA\Property(property="task_number_pattern", type="string", example="{$year}-{$counter}", description="Allows customisation of the task number pattern"),
* @OA\Property(property="task_number_counter", type="integer", example="1", description="____________"),
+ * @OA\Property(property="reminder_send_time", type="integer", example="32400", description="Time from UTC +0 when the email will be sent to the client"),
* @OA\Property(property="expense_number_pattern", type="string", example="{$year}-{$counter}", description="Allows customisation of the expense number pattern"),
* @OA\Property(property="expense_number_counter", type="integer", example="1", description="____________"),
* @OA\Property(property="vendor_number_pattern", type="string", example="{$year}-{$counter}", description="Allows customisation of the vendor number pattern"),
diff --git a/app/Http/Controllers/OpenAPI/InvoiceSchema.php b/app/Http/Controllers/OpenAPI/InvoiceSchema.php
index 3b685acee839..0ec8d8f0f185 100644
--- a/app/Http/Controllers/OpenAPI/InvoiceSchema.php
+++ b/app/Http/Controllers/OpenAPI/InvoiceSchema.php
@@ -35,6 +35,7 @@
* @OA\Property(property="is_deleted", type="boolean", example=true, description="_________"),
* @OA\Property(property="uses_inclusive_taxes", type="boolean", example=true, description="Defines the type of taxes used as either inclusive or exclusive"),
* @OA\Property(property="date", type="string", format="date", example="1994-07-30", description="The Invoice Date"),
+ * @OA\Property(property="next_send_date", type="string", format="date", example="1994-07-30", description="The Next date for a reminder to be sent"),
* @OA\Property(property="partial_due_date", type="string", format="date", example="1994-07-30", description="_________"),
* @OA\Property(property="due_date", type="string", format="date", example="1994-07-30", description="_________"),
* @OA\Property(property="settings",ref="#/components/schemas/CompanySettings"),
diff --git a/app/Http/Controllers/OpenAPI/PaymentSchema.php b/app/Http/Controllers/OpenAPI/PaymentSchema.php
index 4dee2496a3c1..6d6896ff4531 100644
--- a/app/Http/Controllers/OpenAPI/PaymentSchema.php
+++ b/app/Http/Controllers/OpenAPI/PaymentSchema.php
@@ -4,7 +4,20 @@
* schema="Payment",
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="______"),
+ * @OA\Property(property="client_id", type="string", example="Opnel5aKBz", description="______"),
+ * @OA\Property(property="invitation_id", type="string", example="Opnel5aKBz", description="______"),
+ * @OA\Property(property="client_contact_id", type="string", example="Opnel5aKBz", description="______"),
+ * @OA\Property(property="user_id", type="string", example="Opnel5aKBz", description="______"),
+ * @OA\Property(property="type_id", type="string", example="1", description="The Payment Type ID"),
+ * @OA\Property(property="date", type="string", example="1-1-2014", description="The Payment date"),
+ * @OA\Property(property="transaction_reference", type="string", example="xcsSxcs124asd", description="The transaction reference as defined by the payment gateway"),
+ * @OA\Property(property="assigned_user_id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="is_manual", type="boolean", example=true, description="______"),
+ * @OA\Property(property="is_deleted", type="boolean", example=true, description="______"),
+ * @OA\Property(property="amount", type="number", example=10.00, description="The amount of this payment"),
* @OA\Property(property="refunded", type="number", example=10.00, description="The refunded amount of this payment"),
+ * @OA\Property(property="updated_at", type="number", format="integer", example="1434342123", description="Timestamp"),
+ * @OA\Property(property="archived_at", type="number", format="integer", example="1434342123", description="Timestamp"),
+ * @OA\Property(property="company_gateway_id", type="string", example="3", description="The company gateway id"),
* )
*/
\ No newline at end of file
diff --git a/app/Http/Controllers/OpenAPI/QuoteSchema.php b/app/Http/Controllers/OpenAPI/QuoteSchema.php
index d51ce09f1d6d..066d7370f9f8 100644
--- a/app/Http/Controllers/OpenAPI/QuoteSchema.php
+++ b/app/Http/Controllers/OpenAPI/QuoteSchema.php
@@ -5,5 +5,6 @@
* type="object",
* @OA\Property(property="id", type="string", example="Opnel5aKBz", description="______"),
* @OA\Property(property="total_taxes", type="number", format="float", example="10.00", description="The total taxes for the quote"),
+ * @OA\Property(property="next_send_date", type="string", format="date", example="1994-07-30", description="The Next date for a reminder to be sent"),
* )
*/
\ No newline at end of file
diff --git a/app/Http/Controllers/PaymentController.php b/app/Http/Controllers/PaymentController.php
index b2b4cbdacd71..4531bc6635fc 100644
--- a/app/Http/Controllers/PaymentController.php
+++ b/app/Http/Controllers/PaymentController.php
@@ -200,7 +200,7 @@ class PaymentController extends BaseController
* format="float",
* ),
* @OA\Property(
- * property="payment_date",
+ * property="date",
* example="2019/12/1",
* description="The payment date",
* type="string",
diff --git a/app/Http/Controllers/TemplateController.php b/app/Http/Controllers/TemplateController.php
index 9a01f2551bc6..f428649dde84 100644
--- a/app/Http/Controllers/TemplateController.php
+++ b/app/Http/Controllers/TemplateController.php
@@ -28,7 +28,7 @@ class TemplateController extends BaseController
* @return \Illuminate\Http\Response
*
* @OA\Post(
- * path="/api/v1/templates/{entity}/{entity_id}",
+ * path="/api/v1/templates",
* operationId="getShowTemplate",
* tags={"templates"},
* summary="Returns a entity template with the template variables replaced with the Entities",
diff --git a/app/Http/Middleware/TokenAuth.php b/app/Http/Middleware/TokenAuth.php
index abe1099cd415..5ae780f20936 100644
--- a/app/Http/Middleware/TokenAuth.php
+++ b/app/Http/Middleware/TokenAuth.php
@@ -75,7 +75,7 @@ class TokenAuth
'errors' => []
];
- return response()->json(json_encode($error, JinvoicelspSON_PRETTY_PRINT) ,403);
+ return response()->json(json_encode($error, JSON_PRETTY_PRINT) ,403);
}
return $next($request);
diff --git a/app/Http/Requests/Payment/StorePaymentRequest.php b/app/Http/Requests/Payment/StorePaymentRequest.php
index 4933149c3cdf..4fa09fb560b6 100644
--- a/app/Http/Requests/Payment/StorePaymentRequest.php
+++ b/app/Http/Requests/Payment/StorePaymentRequest.php
@@ -63,7 +63,7 @@ class StorePaymentRequest extends Request
$rules = [
'amount' => 'numeric|required',
- 'payment_date' => 'required',
+ 'date' => 'required',
'client_id' => 'required',
'invoices' => new ValidPayableInvoicesRule(),
];
diff --git a/app/Http/Requests/Payment/UpdatePaymentRequest.php b/app/Http/Requests/Payment/UpdatePaymentRequest.php
index 4c184310d9d0..b63c65e2dfbe 100644
--- a/app/Http/Requests/Payment/UpdatePaymentRequest.php
+++ b/app/Http/Requests/Payment/UpdatePaymentRequest.php
@@ -35,9 +35,9 @@ class UpdatePaymentRequest extends Request
return [
'documents' => 'mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx',
'client_id' => 'integer|nullable',
- 'payment_type_id' => 'integer|nullable',
+ 'type_id' => 'integer|nullable',
'amount' => 'numeric',
- 'payment_date' => 'required',
+ 'date' => 'required',
];
}
diff --git a/app/Mail/TemplateEmail.php b/app/Mail/TemplateEmail.php
index a7f1badf69c9..99fda55470f5 100644
--- a/app/Mail/TemplateEmail.php
+++ b/app/Mail/TemplateEmail.php
@@ -18,11 +18,15 @@ class TemplateEmail extends Mailable
private $user; //the user the email will be sent from
- public function __construct($message, $template, $user)
+ private $client;
+
+
+ public function __construct($message, $template, $user, $client)
{
$this->message = $message;
$this->template = $template;
- $this->user = $user;
+ $this->user = $user; //this is inappropriate here, need to refactor 'user' in this context the 'user' could also be the 'system'
+ $this->client = $client;
}
/**
@@ -37,12 +41,19 @@ class TemplateEmail extends Mailable
//if using a system level template
$template_name = 'email.template.'.$this->template;
+ $settings = $this->client->getMergedSettings();
+
+ $company = $this->client->company;
+
return $this->from($this->user->email, $this->user->present()->name()) //todo this needs to be fixed to handle the hosted version
->subject($this->message['subject'])
+ ->text('email.template.plain', ['body' => $this->message['body'], 'footer' => $this->message['footer']])
->view($template_name, [
'body' => $this->message['body'],
'footer' => $this->message['footer'],
'title' => $this->message['title'],
+ 'settings' => $settings,
+ 'company' => $company
]);
}
diff --git a/app/Models/Client.php b/app/Models/Client.php
index b96eb36463e6..c1bfa0cb95b6 100644
--- a/app/Models/Client.php
+++ b/app/Models/Client.php
@@ -75,7 +75,7 @@ class Client extends BaseModel
'custom_value1',
'custom_value2',
'custom_value3',
- 'custom_value4,',
+ 'custom_value4',
'shipping_address1',
'shipping_address2',
'shipping_city',
diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php
index 3fecdb61bc46..44dcc2cd93d5 100644
--- a/app/Models/Invoice.php
+++ b/app/Models/Invoice.php
@@ -22,6 +22,7 @@ use App\Models\Currency;
use App\Models\Filterable;
use App\Models\PaymentTerm;
use App\Utils\Number;
+use App\Utils\Traits\InvoiceEmailBuilder;
use App\Utils\Traits\MakesDates;
use App\Utils\Traits\MakesInvoiceValues;
use App\Utils\Traits\NumberFormatter;
@@ -41,7 +42,8 @@ class Invoice extends BaseModel
use MakesDates;
use PresentableTrait;
use MakesInvoiceValues;
-
+ use InvoiceEmailBuilder;
+
protected $presenter = 'App\Models\Presenters\InvoicePresenter';
protected $hidden = [
@@ -461,35 +463,4 @@ class Invoice extends BaseModel
});
}
- /**
- * @deprecated
- *
- * we can use the trait -> makeValues()
- *
- */
- public function getVariables() :array
- {
-
- return [
- '$number' => $this->number,
- '$amount' => $this->amount,
- '$date' => $this->date,
- '$due_date' => $this->due_date,
- '$balance' => $this->balance,
- '$status' => $this->textStatus(),
- '$invoice.number' => $this->number,
- '$invoice.amount' => $this->amount,
- '$invoice.date' => $this->date,
- '$invoice.due_date' => $this->due_date,
- '$invoice.balance' => $this->balance,
- '$invoice.status' => $this->textStatus(),
- ];
-
- }
-
- public function getVariableByKey($key)
- {
-
- }
-
}
diff --git a/app/Models/Payment.php b/app/Models/Payment.php
index a3dce29490f5..a75b634c5822 100644
--- a/app/Models/Payment.php
+++ b/app/Models/Payment.php
@@ -14,6 +14,7 @@ namespace App\Models;
use App\Models\BaseModel;
use App\Models\DateFormat;
use App\Models\Filterable;
+use App\Models\Paymentable;
use App\Utils\Number;
use App\Utils\Traits\MakesDates;
use App\Utils\Traits\MakesHash;
@@ -51,9 +52,9 @@ class Payment extends BaseModel
protected $fillable = [
'client_id',
- 'payment_type_id',
+ 'type_id',
'amount',
- 'payment_date',
+ 'date',
'transaction_reference'
];
@@ -102,7 +103,12 @@ class Payment extends BaseModel
public function type()
{
- return $this->hasOne(PaymentType::class,'id','payment_type_id');
+ return $this->hasOne(PaymentType::class,'id','type_id');
+ }
+
+ public function paymentables()
+ {
+ return $this->hasMany(Paymentable::class);
}
public function formattedAmount()
@@ -112,12 +118,12 @@ class Payment extends BaseModel
public function clientPaymentDate()
{
- if(!$this->payment_date)
+ if(!$this->date)
return '';
$date_format = DateFormat::find($this->client->getSetting('date_format_id'));
- return $this->createClientDate($this->payment_date, $this->client->timezone()->name)->format($date_format->format);
+ return $this->createClientDate($this->date, $this->client->timezone()->name)->format($date_format->format);
}
public static function badgeForStatus(int $status)
diff --git a/app/Models/Paymentable.php b/app/Models/Paymentable.php
index 6ebcef077ee0..65b77493b5bb 100644
--- a/app/Models/Paymentable.php
+++ b/app/Models/Paymentable.php
@@ -19,5 +19,7 @@ class Paymentable extends Pivot
// public $incrementing = true;
+ protected $table = 'paymentables';
+
}
\ No newline at end of file
diff --git a/app/Models/Presenters/CompanyPresenter.php b/app/Models/Presenters/CompanyPresenter.php
index dc5dcbbfb379..7014b39fc177 100644
--- a/app/Models/Presenters/CompanyPresenter.php
+++ b/app/Models/Presenters/CompanyPresenter.php
@@ -11,6 +11,8 @@
namespace App\Models\Presenters;
+use App\Models\Country;
+
/**
* Class CompanyPresenter
* @package App\Models\Presenters
@@ -26,47 +28,56 @@ class CompanyPresenter extends EntityPresenter
return $this->entity->name ?: ctrans('texts.untitled_account');
}
- public function logo()
+ public function logo($settings = null)
{
- return strlen($this->entity->getLogo() > 0) ? $this->entity->getLogo() : 'https://www.invoiceninja.com/wp-content/uploads/2019/01/InvoiceNinja-Logo-Round-300x300.png';
+ if(!$settings)
+ $settings = $this->entity->settings;
+
+ return strlen($settings->company_logo > 0) ? $settings->company_logo : 'https://www.invoiceninja.com/wp-content/uploads/2019/01/InvoiceNinja-Logo-Round-300x300.png';
}
- public function address()
+ public function address($settings = null)
{
$str = '';
$company = $this->entity;
- if ($address1 = $company->settings->address1) {
+ if(!$settings)
+ $settings = $this->entity->settings;
+
+ if ($address1 = $settings->address1) {
$str .= e($address1) . '
';
}
- if ($address2 = $company->settings->address2) {
+ if ($address2 = $settings->address2) {
$str .= e($address2) . '
';
}
- if ($cityState = $this->getCompanyCityState()) {
+ if ($cityState = $this->getCompanyCityState($settings)) {
$str .= e($cityState) . '
';
}
- if ($country = $company->country()) {
+ if ($country = Country::find($settings->country_id)->first()) {
$str .= e($country->name) . '
';
}
- if ($company->settings->phone) {
- $str .= ctrans('texts.work_phone') . ": ". e($company->settings->phone) .'
';
+ if ($settings->phone) {
+ $str .= ctrans('texts.work_phone') . ": ". e($settings->phone) .'
';
}
- if ($company->settings->email) {
- $str .= ctrans('texts.work_email') . ": ". e($company->settings->email) .'
';
+ if ($settings->email) {
+ $str .= ctrans('texts.work_email') . ": ". e($settings->email) .'
';
}
return $str;
}
- public function getCompanyCityState()
+ public function getCompanyCityState($settings = null)
{
- $company = $this->entity;
+ if(!$settings)
+ $settings = $this->entity->settings;
- $swap = $company->country() && $company->country()->swap_postal_code;
+ $country = Country::find($settings->country_id)->first();
- $city = e($company->settings->city);
- $state = e($company->settings->state);
- $postalCode = e($company->settings->postal_code);
+ $swap = $country && $country->swap_postal_code;
+
+ $city = e($settings->city);
+ $state = e($settings->state);
+ $postalCode = e($settings->postal_code);
if ($city || $state || $postalCode) {
return $this->cityStateZip($city, $state, $postalCode, $swap);
diff --git a/app/PaymentDrivers/BasePaymentDriver.php b/app/PaymentDrivers/BasePaymentDriver.php
index d6c2ce93f304..13a0d2f23e74 100644
--- a/app/PaymentDrivers/BasePaymentDriver.php
+++ b/app/PaymentDrivers/BasePaymentDriver.php
@@ -225,7 +225,7 @@ class BasePaymentDriver
$payment->client_id = $this->client->id;
$payment->company_gateway_id = $this->company_gateway->id;
$payment->status_id = Payment::STATUS_COMPLETED;
- $payment->payment_date = Carbon::now();
+ $payment->date = Carbon::now();
return $payment;
diff --git a/app/PaymentDrivers/PayPalExpressPaymentDriver.php b/app/PaymentDrivers/PayPalExpressPaymentDriver.php
index f7a491168a85..3ad46f1335a3 100644
--- a/app/PaymentDrivers/PayPalExpressPaymentDriver.php
+++ b/app/PaymentDrivers/PayPalExpressPaymentDriver.php
@@ -276,7 +276,7 @@ class PayPalExpressPaymentDriver extends BasePaymentDriver
$client_contact_id = $client_contact ? $client_contact->id : null;
$payment->amount = $data['PAYMENTINFO_0_AMT'];
- $payment->payment_type_id = PaymentType::PAYPAL;
+ $payment->type_id = PaymentType::PAYPAL;
$payment->transaction_reference = $data['PAYMENTINFO_0_TRANSACTIONID'];
$payment->client_contact_id = $client_contact_id;
$payment->save();
diff --git a/app/PaymentDrivers/StripePaymentDriver.php b/app/PaymentDrivers/StripePaymentDriver.php
index 4a24dc52c9a6..fdd2b94e2002 100644
--- a/app/PaymentDrivers/StripePaymentDriver.php
+++ b/app/PaymentDrivers/StripePaymentDriver.php
@@ -408,7 +408,7 @@ class StripePaymentDriver extends BasePaymentDriver
$client_contact_id = $client_contact ? $client_contact->id : null;
$payment->amount = $this->convertFromStripeAmount($data['amount'], $this->client->currency()->precision);
- $payment->payment_type_id = $data['payment_type'];
+ $payment->type_id = $data['payment_type'];
$payment->transaction_reference = $data['payment_method'];
$payment->client_contact_id = $client_contact_id;
$payment->save();
diff --git a/app/Transformers/DocumentTransformer.php b/app/Transformers/DocumentTransformer.php
new file mode 100644
index 000000000000..42c13a6edcd8
--- /dev/null
+++ b/app/Transformers/DocumentTransformer.php
@@ -0,0 +1,56 @@
+serializer = $serializer;
+ }
+
+ public function transform(Document $document)
+ {
+ return [
+ 'id' => $this->encodePrimaryKey($document->id),
+ 'user_id' => $this->encodePrimaryKey($document->user_id),
+ 'assigned_user_id' => $this->encodePrimaryKey($document->assigned_user_id),
+ 'project_id' => $this->encodePrimaryKey($document->project_id),
+ 'vendor_id' => $this->encodePrimaryKey($document->vendor_id),
+ 'path' => (string) $document->path ?: '',
+ 'preview' => (string) $document->preview ?: '',
+ 'name' => (string) $document->name,
+ 'type' => (string) $document->type,
+ 'disk' => (string) $document->disk,
+ 'hash' => (string) $document->hash,
+ 'size' => (int) $document->size,
+ 'width' => (int) $document->width,
+ 'height' => (int) $document->height,
+ 'is_default' => (bool) $document->is_default,
+ 'updated_at' => (int) $document->updated_at,
+ 'archived_at' => (int) $document->archived_at
+
+ ];
+ }
+}
+
\ No newline at end of file
diff --git a/app/Transformers/InvoiceTransformer.php b/app/Transformers/InvoiceTransformer.php
index 52011b9cc212..b0b3a9dab830 100644
--- a/app/Transformers/InvoiceTransformer.php
+++ b/app/Transformers/InvoiceTransformer.php
@@ -96,6 +96,7 @@ class InvoiceTransformer extends EntityTransformer
'discount' => (float) $invoice->discount,
'po_number' => $invoice->po_number ?: '',
'date' => $invoice->date ?: '',
+ 'next_send_date' => $invoice->date ?: '',
'due_date' => $invoice->due_date ?: '',
'terms' => $invoice->terms ?: '',
'public_notes' => $invoice->public_notes ?: '',
diff --git a/app/Transformers/PaymentTransformer.php b/app/Transformers/PaymentTransformer.php
index 567cd2ace879..8da1be54d7a7 100644
--- a/app/Transformers/PaymentTransformer.php
+++ b/app/Transformers/PaymentTransformer.php
@@ -15,6 +15,8 @@ use App\Models\Account;
use App\Models\Client;
use App\Models\Invoice;
use App\Models\Payment;
+use App\Models\Paymentable;
+use App\Transformers\PaymentableTransformer;
use App\Utils\Traits\MakesHash;
class PaymentTransformer extends EntityTransformer
@@ -28,6 +30,7 @@ class PaymentTransformer extends EntityTransformer
protected $availableIncludes = [
'client',
'invoices',
+ 'paymentables'
];
public function __construct($serializer = null)
@@ -50,7 +53,15 @@ class PaymentTransformer extends EntityTransformer
return $this->includeItem($payment->client, $transformer, Client::class);
}
-//todo incomplete
+
+ public function includePaymentables(Payment $payment)
+ {
+ $transformer = new PaymentableTransformer($this->serializer);
+
+ return $this->includeCollection($payment->paymentables, $transformer, Paymentable::class);
+ }
+
+
public function transform(Payment $payment)
{
return [
@@ -58,14 +69,22 @@ class PaymentTransformer extends EntityTransformer
'user_id' => $this->encodePrimaryKey($payment->user_id),
'assigned_user_id' => $this->encodePrimaryKey($payment->assigned_user_id),
'amount' => (float) $payment->amount,
+ 'refunded' => (float) $payment->refunded,
'transaction_reference' => $payment->transaction_reference ?: '',
- 'payment_date' => $payment->payment_date ?: '',
+ 'date' => $payment->date ?: '',
+ 'is_manual' => (bool) $payment->is_manual,
'updated_at' => $payment->updated_at,
'archived_at' => $payment->deleted_at,
'is_deleted' => (bool) $payment->is_deleted,
- 'payment_type_id' => (string) $payment->payment_type_id ?: '',
+ 'type_id' => (string) $payment->payment_type_id ?: '',
'invitation_id' => (string) $payment->invitation_id ?: '',
'client_id' => (string) $this->encodePrimaryKey($payment->client_id),
+ 'client_contact_id' => (string) $this->encodePrimaryKey($payment->client_contact_id),
+ 'company_gateway_id' => (string) $this->encodePrimaryKey($payment->company_gateway_id),
+ 'status_id'=> (string) $payment->status_id,
+ 'type_id'=> (string) $payment->type_id,
+ 'project_id' => (string) $this->encodePrimaryKey($payment->project_id),
+ 'vendor_id' => (string) $this->encodePrimaryKey($payment->vendor_id),
/*
'private_notes' => $payment->private_notes ?: '',
'exchange_rate' => (float) $payment->exchange_rate,
diff --git a/app/Transformers/PaymentableTransformer.php b/app/Transformers/PaymentableTransformer.php
new file mode 100644
index 000000000000..989d26838cc3
--- /dev/null
+++ b/app/Transformers/PaymentableTransformer.php
@@ -0,0 +1,43 @@
+serializer = $serializer;
+
+ }
+
+ public function transform(Paymentable $paymentable)
+ {
+ return [
+ 'id' => $this->encodePrimaryKey($paymentable->id),
+ 'invoice_id' => $this->encodePrimaryKey($paymentable->paymentable_id),
+ 'amount' => $paymentable->amount,
+ ];
+ }
+}
diff --git a/app/Transformers/QuoteTransformer.php b/app/Transformers/QuoteTransformer.php
index 976c93f86638..2f369c226fd9 100644
--- a/app/Transformers/QuoteTransformer.php
+++ b/app/Transformers/QuoteTransformer.php
@@ -93,6 +93,7 @@ class QuoteTransformer extends EntityTransformer
'discount' => (float) $quote->discount ?: '',
'po_number' => $quote->po_number ?: '',
'quote_date' => $quote->quote_date ?: '',
+ 'next_send_date' => $quote->date ?: '',
'valid_until' => $quote->valid_until ?: '',
'terms' => $quote->terms ?: '',
'public_notes' => $quote->public_notes ?: '',
diff --git a/app/Utils/Traits/InvoiceEmailBuilder.php b/app/Utils/Traits/InvoiceEmailBuilder.php
new file mode 100644
index 000000000000..f3b2e825f8ea
--- /dev/null
+++ b/app/Utils/Traits/InvoiceEmailBuilder.php
@@ -0,0 +1,120 @@
+client;
+
+ if(!$reminder_template)
+ $reminder_template = $this->calculateTemplate();
+
+ //Need to determine which email template we are producing
+ $email_data = $this->generateTemplateData($reminder_template);
+
+ return $email_data;
+
+ }
+
+ private function generateTemplateData(string $reminder_template) :array
+ {
+ $data = [];
+
+ $client = $this->client;
+
+ $body_template = $client->getSetting('email_template_'.$reminder_template);
+ $subject_template = $client->getSetting('email_subject_'.$reminder_template);
+
+ $data['body'] = $this->parseTemplate($body_template, false);
+ $data['subject'] = $this->parseTemplate($subject_template, true);
+
+ return $data;
+ }
+
+ private function parseTemplate(string $template_data, bool $is_markdown = true) :string
+ {
+ $invoice_variables = $this->makeValues();
+
+ //process variables
+ $data = str_replace(array_keys($invoice_variables), array_values($invoice_variables), $template_data);
+
+ //process markdown
+ if($is_markdown)
+ $data = Parsedown::instance()->line($data);
+
+ return $data;
+ }
+
+ private function calculateTemplate() :string
+ {
+ //if invoice is currently a draft, or being marked as sent, this will be the initial email
+ $client = $this->client;
+
+ //if the invoice
+ if($this->status_id == Invoice::STATUS_DRAFT || Carbon::parse($this->due_date) > now())
+ {
+ return 'invoice';
+ }
+ else if($client->getSetting('enable_reminder1') !== false && $this->inReminderWindow($client->getSetting('schedule_reminder1'), $client->getSetting('num_days_reminder1')))
+ {
+ return 'template1';
+ }
+ else if($client->getSetting('enable_reminder2') !== false && $this->inReminderWindow($client->getSetting('schedule_reminder2'), $client->getSetting('num_days_reminder2')))
+ {
+ return 'template2';
+ }
+ else if($client->getSetting('enable_reminder3') !== false && $this->inReminderWindow($client->getSetting('schedule_reminder3'), $client->getSetting('num_days_reminder3')))
+ {
+ return 'template3';
+ }
+ //also implement endless reminders here
+ //
+
+ }
+
+ private function inReminderWindow($schedule_reminder, $num_days_reminder)
+ {
+ switch ($schedule_reminder) {
+ case 'after_invoice_date':
+ return Carbon::parse($this->date)->addDays($num_days_reminder)->startOfDay()->eq(Carbon::now()->startOfDay());
+ break;
+ case 'before_due_date':
+ return Carbon::parse($this->due_date)->subDays($num_days_reminder)->startOfDay()->eq(Carbon::now()->startOfDay());
+ break;
+ case 'after_due_date':
+ return Carbon::parse($this->due_date)->addDays($num_days_reminder)->startOfDay()->eq(Carbon::now()->startOfDay());
+ break;
+ default:
+ # code...
+ break;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/Utils/Traits/MakesInvoiceValues.php b/app/Utils/Traits/MakesInvoiceValues.php
index 64cfccdc7ee2..9a7d22816e49 100644
--- a/app/Utils/Traits/MakesInvoiceValues.php
+++ b/app/Utils/Traits/MakesInvoiceValues.php
@@ -11,6 +11,7 @@
namespace App\Utils\Traits;
+use App\Models\Country;
use App\Utils\Number;
/**
@@ -158,7 +159,9 @@ trait MakesInvoiceValues
throw new Exception(debug_backtrace()[1]['function'], 1);
exit;
}
-
+
+ $settings = $this->client->getMergedSettings();
+
$data = [];
$data['$date'] = $this->date;
@@ -166,9 +169,13 @@ trait MakesInvoiceValues
$data['$due_date'] = $this->due_date;
$data['$invoice.due_date'] = &$data['$due_date'];
$data['$number'] = $this->number;
+ $data['$invoice.number'] = &$data['$number'];
$data['$po_number'] = $this->po_number;
+ $data['$invoice.po_number'] = &$data['$po_number'];
$data['$line_taxes'] = $this->makeLineTaxes();
+ $data['$invoice.line_taxes'] = &$data['$line_taxes'];
$data['$total_taxes'] = $this->makeTotalTaxes();
+ $data['$invoice.total_taxes'] = &$data['$total_taxes'];
// $data['$tax'] = ;
// $data['$item'] = ;
// $data['$description'] = ;
@@ -179,12 +186,22 @@ trait MakesInvoiceValues
$data['$discount'] = Number::formatMoney($this->calc()->getTotalDiscount(), $this->client);
$data['$invoice.discount'] = &$data['$discount'];
$data['$subtotal'] = Number::formatMoney($this->calc()->getSubTotal(), $this->client);
+ $data['$invoice.subtotal'] = &$data['$subtotal'];
$data['$balance_due'] = Number::formatMoney($this->balance, $this->client);
+ $data['$invoice.balance_due'] = &$data['$balance_due'];
$data['$partial_due'] = Number::formatMoney($this->partial, $this->client);
+ $data['$invoice.partial_due'] = &$data['$partial_due'];
$data['$total'] = Number::formatMoney($this->calc()->getTotal(), $this->client);
+ $data['$invoice.total'] = &$data['$total'];
+ $data['$amount'] = &$data['$total'];
+ $data['$invoice.amount'] = &$data['$total'];
+
$data['$balance'] = Number::formatMoney($this->calc()->getBalance(), $this->client);
+ $data['$invoice.balance'] = &$data['$balance'];
$data['$taxes'] = Number::formatMoney($this->calc()->getItemTotalTaxes(), $this->client);
+ $data['$invoice.taxes'] = &$data['$taxes'];
$data['$terms'] = $this->terms;
+ $data['$invoice.terms'] = &$data['$terms'];
// $data['$your_invoice'] = ;
// $data['$quote'] = ;
// $data['$your_quote'] = ;
@@ -200,34 +217,48 @@ trait MakesInvoiceValues
// $data['$quote_to'] = ;
// $data['$details'] = ;
$data['$invoice_no'] = $this->number;
+ $data['$invoice.invoice_no'] = &$data['$invoice_no'];
// $data['$quote_no'] = ;
// $data['$valid_until'] = ;
$data['$client_name'] = $this->present()->clientName();
+ $data['$client.name'] = &$data['$client_name'];
$data['$client_address'] = $this->present()->address();
+ $data['$client.address'] = &$data['$client_address'];
$data['$address1'] = $this->client->address1;
+ $data['$client.address1'] = &$data['$address1'];
$data['$address2'] = $this->client->address2;
+ $data['$client.address2'] = &$data['$address2'];
$data['$id_number'] = $this->client->id_number;
+ $data['$client.id_number'] = &$data['$id_number'];
$data['$vat_number'] = $this->client->vat_number;
+ $data['$client.vat_number'] = &$data['$vat_number'];
$data['$website'] = $this->client->present()->website();
+ $data['$client.website'] = &$data['$website'];
$data['$phone'] = $this->client->present()->phone();
+ $data['$client.phone'] = &$data['$phone'];
$data['$city_state_postal'] = $this->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, FALSE);
+ $data['$client.city_state_postal'] = &$data['$city_state_postal'];
$data['$postal_city_state'] = $this->present()->cityStateZip($this->client->city, $this->client->state, $this->client->postal_code, TRUE);
+ $data['$client.postal_city_state'] = &$data['$postal_city_state'];
$data['$country'] = isset($this->client->country->name) ?: 'No Country Set';
+ $data['$client.country'] = &$data['$country'];
$data['$email'] = isset($this->client->primary_contact()->first()->email) ?: 'no contact email on record';
+ $data['$client.email'] = &$data['$email'];
$data['$contact_name'] = $this->client->present()->primary_contact_name();
- $data['$company_name'] = $this->company->present()->name();
- $data['$company_address1'] = $this->company->address1;
- $data['$company_address2'] = $this->company->address2;
- $data['$company_city'] = $this->company->city;
- $data['$company_state'] = $this->company->state;
- $data['$company_postal_code'] = $this->company->postal_code;
- $data['$company_country'] = $this->company->country() ? $this->company->country()->name : '';
- $data['$company_phone'] = $this->company->phone;
- $data['$company_email'] = $this->company->email;
- $data['$company_vat_number'] = $this->company->vat_number;
- $data['$company_id_number'] = $this->company->id_number;
- $data['$company_address'] = $this->company->present()->address();
- $data['$company_logo'] = $this->company->present()->logo();
+ $data['$contact.name'] = &$data['$contact_name'];
+ $data['$company.name'] = $this->company->present()->name();
+ $data['$company.address1'] = $settings->address1;
+ $data['$company.address2'] = $settings->address2;
+ $data['$company.city'] = $settings->city;
+ $data['$company.state'] = $settings->state;
+ $data['$company.postal_code'] = $settings->postal_code;
+ $data['$company.country'] = Country::find($settings->country_id)->first()->name;
+ $data['$company.phone'] = $settings->phone;
+ $data['$company.email'] = $settings->email;
+ $data['$company.vat_number'] = $settings->vat_number;
+ $data['$company.id_number'] = $settings->id_number;
+ $data['$company.address'] = $this->company->present()->address($settings);
+ $data['$company.logo'] = $this->company->present()->logo($settings);
//$data['$blank'] = ;
//$data['$surcharge'] = ;
/*
diff --git a/database/factories/PaymentFactory.php b/database/factories/PaymentFactory.php
index 32201adb5e56..a37b872c3b72 100644
--- a/database/factories/PaymentFactory.php
+++ b/database/factories/PaymentFactory.php
@@ -10,9 +10,9 @@ $factory->define(App\Models\Payment::class, function (Faker $faker) {
return [
'is_deleted' => false,
'amount' => $faker->numberBetween(1,10),
- 'payment_date' => $faker->date(),
+ 'date' => $faker->date(),
'transaction_reference' => $faker->text(10),
- 'payment_type_id' => Payment::TYPE_CREDIT_CARD,
+ 'type_id' => Payment::TYPE_CREDIT_CARD,
'status_id' => Payment::STATUS_COMPLETED
];
diff --git a/database/migrations/2014_10_13_000000_create_users_table.php b/database/migrations/2014_10_13_000000_create_users_table.php
index 458fc9586fe3..b780c77045fa 100644
--- a/database/migrations/2014_10_13_000000_create_users_table.php
+++ b/database/migrations/2014_10_13_000000_create_users_table.php
@@ -211,7 +211,10 @@ class CreateUsersTable extends Migration
Schema::create('documents', function (Blueprint $table){
$table->increments('id');
$table->unsignedInteger('user_id');
+ $table->unsignedInteger('assigned_user_id');
$table->unsignedInteger('company_id')->index();
+ $table->unsignedInteger('project_id')->nullable();
+ $table->unsignedInteger('vendor_id')->nullable();
$table->string('path')->nullable();
$table->string('preview')->nullable();
$table->string('name')->nullable();
@@ -222,6 +225,10 @@ class CreateUsersTable extends Migration
$table->unsignedInteger('width')->nullable();
$table->unsignedInteger('height')->nullable();
$table->boolean('is_default')->default(0);
+ $table->string('custom_value1')->nullable();
+ $table->string('custom_value2')->nullable();
+ $table->string('custom_value3')->nullable();
+ $table->string('custom_value4')->nullable();
$table->unsignedInteger('documentable_id');
$table->string('documentable_type');
@@ -258,7 +265,11 @@ class CreateUsersTable extends Migration
$table->mediumText('signature')->nullable();
$table->string('password');
$table->rememberToken();
-
+ $table->string('custom_value1')->nullable();
+ $table->string('custom_value2')->nullable();
+ $table->string('custom_value3')->nullable();
+ $table->string('custom_value4')->nullable();
+
$table->timestamps(6);
$table->softDeletes('deleted_at', 6);
@@ -378,6 +389,22 @@ class CreateUsersTable extends Migration
});
+ Schema::create('projects', function ($t) {
+ $t->increments('id');
+ $t->unsignedInteger('user_id');
+ $t->unsignedInteger('assigned_user_id');
+ $t->unsignedInteger('company_id')->index();
+ $t->unsignedInteger('client_id')->nullable();
+ $t->string('name');
+ $t->string('description');
+ $t->timestamps();
+ $t->softDeletes();
+
+ $t->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
+ $t->foreign('company_id')->references('id')->on('companies');
+
+ });
+
Schema::create('company_gateways', function($table)
{
$table->increments('id');
@@ -391,7 +418,11 @@ class CreateUsersTable extends Migration
$table->boolean('update_details')->default(false)->nullable();
$table->mediumText('config');
$table->text('fees_and_limits');
-
+ $table->string('custom_value1')->nullable();
+ $table->string('custom_value2')->nullable();
+ $table->string('custom_value3')->nullable();
+ $table->string('custom_value4')->nullable();
+
$table->timestamps(6);
$table->softDeletes('deleted_at', 6);
@@ -410,7 +441,8 @@ class CreateUsersTable extends Migration
$t->unsignedInteger('assigned_user_id')->nullable();
$t->unsignedInteger('company_id')->index();
$t->unsignedInteger('status_id');
-
+ $t->unsignedInteger('project_id')->nullable();
+ $t->unsignedInteger('vendor_id')->nullable();
$t->unsignedInteger('recurring_id')->nullable();
$t->unsignedInteger('design_id')->nullable();
@@ -449,6 +481,7 @@ class CreateUsersTable extends Migration
$t->string('custom_value2')->nullable();
$t->string('custom_value3')->nullable();
$t->string('custom_value4')->nullable();
+ $t->datetime('next_send_date')->nullable();
$t->string('custom_surcharge1')->nullable();
$t->string('custom_surcharge2')->nullable();
@@ -482,6 +515,8 @@ class CreateUsersTable extends Migration
$t->unsignedInteger('user_id');
$t->unsignedInteger('assigned_user_id')->nullable();
$t->unsignedInteger('company_id')->index();
+ $t->unsignedInteger('project_id')->nullable();
+ $t->unsignedInteger('vendor_id')->nullable();
$t->unsignedInteger('status_id')->index();
$t->text('number')->nullable();
@@ -548,7 +583,8 @@ class CreateUsersTable extends Migration
$t->unsignedInteger('user_id');
$t->unsignedInteger('assigned_user_id')->nullable();
$t->unsignedInteger('company_id')->index();
-
+ $t->unsignedInteger('project_id')->nullable();
+ $t->unsignedInteger('vendor_id')->nullable();
$t->unsignedInteger('status_id')->index();
$t->float('discount')->default(0);
@@ -613,7 +649,8 @@ class CreateUsersTable extends Migration
$t->unsignedInteger('assigned_user_id')->nullable();
$t->unsignedInteger('company_id')->index();
$t->unsignedInteger('status_id');
-
+ $t->unsignedInteger('project_id')->nullable();
+ $t->unsignedInteger('vendor_id')->nullable();
$t->unsignedInteger('recurring_id')->nullable();
$t->unsignedInteger('design_id')->nullable();
@@ -624,6 +661,7 @@ class CreateUsersTable extends Migration
$t->string('po_number')->nullable();
$t->date('date')->nullable();
$t->datetime('due_date')->nullable();
+ $t->datetime('next_send_date')->nullable();
$t->boolean('is_deleted')->default(false);
@@ -731,7 +769,8 @@ class CreateUsersTable extends Migration
$t->unsignedInteger('company_id')->index();
$t->unsignedInteger('user_id');
$t->unsignedInteger('assigned_user_id')->nullable();
-
+ $t->unsignedInteger('project_id')->nullable();
+ $t->unsignedInteger('vendor_id')->nullable();
$t->string('custom_value1')->nullable();
$t->string('custom_value2')->nullable();
$t->string('custom_value3')->nullable();
@@ -765,16 +804,18 @@ class CreateUsersTable extends Migration
$t->increments('id');
$t->unsignedInteger('company_id')->index();
$t->unsignedInteger('client_id')->index();
+ $t->unsignedInteger('project_id')->nullable();
+ $t->unsignedInteger('vendor_id')->nullable();
$t->unsignedInteger('user_id')->nullable();
$t->unsignedInteger('assigned_user_id')->nullable();
$t->unsignedInteger('client_contact_id')->nullable();
$t->unsignedInteger('invitation_id')->nullable();
$t->unsignedInteger('company_gateway_id')->nullable();
- $t->unsignedInteger('payment_type_id')->nullable();
+ $t->unsignedInteger('type_id')->nullable();
$t->unsignedInteger('status_id')->index();
$t->decimal('amount', 16, 4)->default(0);
$t->decimal('refunded', 16, 4)->default(0);
- $t->datetime('payment_date')->nullable();
+ $t->date('date')->nullable();
$t->string('transaction_reference')->nullable();
$t->string('payer_id')->nullable();
$t->timestamps(6);
@@ -788,7 +829,7 @@ class CreateUsersTable extends Migration
$t->foreign('company_gateway_id')->references('id')->on('company_gateways')->onDelete('cascade');
$t->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
- $t->foreign('payment_type_id')->references('id')->on('payment_types');
+ $t->foreign('type_id')->references('id')->on('payment_types');
});
@@ -819,6 +860,9 @@ class CreateUsersTable extends Migration
$table->unsignedInteger('company_id')->index();
$table->unsignedInteger('client_id')->nullable();
$table->unsignedInteger('invoice_id')->nullable();
+ $table->unsignedInteger('project_id')->nullable();
+ $table->unsignedInteger('vendor_id')->nullable();
+
$table->timestamps(6);
$table->softDeletes('deleted_at', 6);
@@ -902,6 +946,8 @@ class CreateUsersTable extends Migration
$table->unsignedInteger('client_id')->nullable();
$table->unsignedInteger('client_contact_id')->nullable();
$table->unsignedInteger('account_id')->nullable();
+ $table->unsignedInteger('project_id')->nullable();
+ $table->unsignedInteger('vendor_id')->nullable();
$table->unsignedInteger('payment_id')->nullable();
$table->unsignedInteger('invoice_id')->nullable();
$table->unsignedInteger('invitation_id')->nullable();
@@ -914,6 +960,8 @@ class CreateUsersTable extends Migration
$table->text('notes');
$table->timestamps(6);
+ $table->index(['vendor_id', 'company_id']);
+ $table->index(['project_id', 'company_id']);
$table->index(['user_id', 'company_id']);
$table->index(['client_id', 'company_id']);
$table->index(['payment_id', 'company_id']);
diff --git a/database/seeds/RandomDataSeeder.php b/database/seeds/RandomDataSeeder.php
index b4af16e80d09..3a47b5b6a18e 100644
--- a/database/seeds/RandomDataSeeder.php
+++ b/database/seeds/RandomDataSeeder.php
@@ -168,13 +168,13 @@ class RandomDataSeeder extends Seeder
if(rand(0, 1)) {
$payment = App\Models\Payment::create([
- 'payment_date' => now(),
+ 'date' => now(),
'user_id' => $user->id,
'company_id' => $company->id,
'client_id' => $client->id,
'amount' => $invoice->balance,
'transaction_reference' => rand(0,500),
- 'payment_type_id' => PaymentType::CREDIT_CARD_OTHER,
+ 'type_id' => PaymentType::CREDIT_CARD_OTHER,
'status_id' => Payment::STATUS_COMPLETED,
]);
diff --git a/resources/views/email/partials/company_logo.blade.php b/resources/views/email/partials/company_logo.blade.php
new file mode 100644
index 000000000000..d0bf15cde2dc
--- /dev/null
+++ b/resources/views/email/partials/company_logo.blade.php
@@ -0,0 +1,11 @@
+@if ($company->present()->logo($settings))
+ @if ($settings->website)
+
+ @endif
+
+
+
+ @if ($settings->website)
+
+ @endif
+@endif
diff --git a/resources/views/email/template/master.blade.php b/resources/views/email/template/master.blade.php
index 19a0e48c9a80..78ca09f2e27b 100644
--- a/resources/views/email/template/master.blade.php
+++ b/resources/views/email/template/master.blade.php
@@ -304,6 +304,9 @@
@include('email.partials.company_logo') | +|
diff --git a/resources/views/email/template/plain.blade.php b/resources/views/email/template/plain.blade.php
index 8b4b1b321bca..c21db58e9be6 100644
--- a/resources/views/email/template/plain.blade.php
+++ b/resources/views/email/template/plain.blade.php
@@ -1,4 +1,4 @@
{{ $body }}
- - + + {{ $footer }} \ No newline at end of file diff --git a/tests/Feature/InvoiceEmailTest.php b/tests/Feature/InvoiceEmailTest.php index 4759e7eae777..d1bfb4bbbb74 100644 --- a/tests/Feature/InvoiceEmailTest.php +++ b/tests/Feature/InvoiceEmailTest.php @@ -2,10 +2,19 @@ namespace Feature; +use App\Mail\TemplateEmail; +use App\Models\ClientContact; +use App\Models\Invoice; +use App\Models\InvoiceInvitation; +use App\Utils\Traits\GeneratesCounter; +use App\Utils\Traits\InvoiceEmailBuilder; use Illuminate\Database\Eloquent\Model; use Illuminate\Foundation\Testing\Concerns\InteractsWithDatabase; use Illuminate\Foundation\Testing\DatabaseTransactions; +use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Session; +use Parsedown; use Tests\MockAccountData; use Tests\TestCase; @@ -16,6 +25,7 @@ class InvoiceEmailTest extends TestCase { use MockAccountData; use DatabaseTransactions; + use GeneratesCounter; public function setUp() :void { @@ -33,119 +43,62 @@ class InvoiceEmailTest extends TestCase public function test_initial_email_sends() { - \Log::error($this->invoice->makeValues()); - } + // \Log::error($this->invoice->makeValues()); + $this->invoice->date = now(); + $this->invoice->due_date = now()->addDays(7); + $this->invoice->number = $this->getNextInvoiceNumber($this->client); + $this->invoice->client = $this->client; + $message_array = $this->invoice->getEmailData(); + $message_array['title'] = &$message_array['subject']; + $message_array['footer'] = 'The Footer'; + $template_style = $this->client->getSetting('email_style'); + $template_style = 'light'; + //iterate through the senders list and send from here + $invitations = InvoiceInvitation::whereInvoiceId($this->invoice->id)->get(); + $invitations->each(function($invitation) use($message_array, $template_style) { + $contact = ClientContact::find($invitation->client_contact_id)->first(); + if($contact->send_invoice && $contact->email) + { + //there may be template variables left over for the specific contact? need to reparse here + + //change the runtime config of the mail provider here: + + //send message + Mail::to($contact->email) + ->send(new TemplateEmail($message_array, $template_style, $this->user, $this->client)); + //fire any events + + sleep(5); + + } - - - - - - - - -//TDD - - /** - * Builds the correct template to send - * @param App\Models\Invoice $invoice The Invoice Model - * @param string $reminder_template The template name ie reminder1 - * @return void - */ - private function invoiceEmailWorkFlow($invoice, $reminder_template = null) - { - //client - $client = $invoice->client; - - $template_style = $client->getSetting('email_style'); - - if(!$reminder_template) - $reminder_template = $this->calculateTemplate($invoice); - - //Need to determine which email template we are producing - $email_data = $this->generateTemplateData($invoice, $reminder_template); - - - } - - private function generateTemplateData(Invoice $invoice, string $reminder_template) :array - { - $data = []; - - $client = $invoice->client; - - $body_template = $client->getSetting('email_template_'.$reminder_template); - $subject_template = $client->getSetting('email_subject_'.$reminder_template); - - $data['message'] = $this->parseTemplate($invoice, $body_template); - $data['subject'] = $this->parseTemplate($invoice, $subject_template); - - return $data; - } - - private function parseTemplate($invoice, $template_data) :string - { - - //process variables + }); - //process markdown - + } - private function calculateTemplate(Invoice $invoice) :string - { - //if invoice is currently a draft, or being marked as sent, this will be the initial email - $client = $invoice->client; - //if the invoice - if($invoice->status_id == Invoice::STATUS_DRAFT || Carbon::parse($invoice->due_date) > now()) - { - return 'invoice'; - } - else if($client->getSetting('enable_reminder1') !== false && $this->inReminderWindow($invoice, $client->getSetting('schedule_reminder1'), $client->getSetting('num_days_reminder1'))) - { - return 'template1'; - } - else if($client->getSetting('enable_reminder2') !== false && $this->inReminderWindow($invoice, $client->getSetting('schedule_reminder2'), $client->getSetting('num_days_reminder2'))) - { - return 'template2'; - } - else if($client->getSetting('enable_reminder3') !== false && $this->inReminderWindow($invoice, $client->getSetting('schedule_reminder3'), $client->getSetting('num_days_reminder3'))) - { - return 'template3'; - } - //also implement endless reminders here - // - - } - private function inReminderWindow($invoice, $schedule_reminder, $num_days_reminder) - { - switch ($schedule_reminder) { - case 'after_invoice_date': - return Carbon::parse($invoice->date)->addDays($num_days_reminder)->startOfDay()->eq(Carbon::now()->startOfDay()); - break; - case 'before_due_date': - return Carbon::parse($invoice->due_date)->subDays($num_days_reminder)->startOfDay()->eq(Carbon::now()->startOfDay()); - break; - case 'after_due_date': - return Carbon::parse($invoice->due_date)->addDays($num_days_reminder)->startOfDay()->eq(Carbon::now()->startOfDay()); - break; - default: - # code... - break; - } - } + + + + + + + + + + } \ No newline at end of file diff --git a/tests/Feature/PaymentTest.php b/tests/Feature/PaymentTest.php index 330e6d4d2507..a22ea7a064b3 100644 --- a/tests/Feature/PaymentTest.php +++ b/tests/Feature/PaymentTest.php @@ -135,7 +135,7 @@ class PaymentTest extends TestCase 'amount' => $this->invoice->amount ], ], - 'payment_date' => '2020/12/11', + 'date' => '2020/12/11', ]; @@ -185,7 +185,7 @@ class PaymentTest extends TestCase 'amount' => $this->invoice->amount ], ], - 'payment_date' => '2020/12/12', + 'date' => '2020/12/12', ]; @@ -245,11 +245,13 @@ class PaymentTest extends TestCase 'amount' => $this->invoice->amount, 'client_id' => $client->hashed_id, 'invoices' => '', - 'payment_date' => '2020/12/12', + 'date' => '2020/12/12', ]; + $response = false; + try { $response = $this->withHeaders([ 'X-API-SECRET' => config('ninja.api_secret'), @@ -300,18 +302,31 @@ class PaymentTest extends TestCase 'amount' => 2.0 ], ], - 'payment_date' => '2019/12/12', + 'date' => '2019/12/12', ]; + $response = false; + + try { $response = $this->withHeaders([ 'X-API-SECRET' => config('ninja.api_secret'), 'X-API-TOKEN' => $this->token, ])->post('/api/v1/payments?include=invoices', $data); - $arr = $response->json(); + + } + catch(ValidationException $e) { + $message = json_decode($e->validator->getMessageBag(),1); + $this->assertNotNull($message); + \Log::error($message); + } + + if($response) { $response->assertStatus(200); + $arr = $response->json(); + $payment_id = $arr['data']['id']; $payment = Payment::find($this->decodePrimaryKey($payment_id))->first(); @@ -325,6 +340,7 @@ class PaymentTest extends TestCase $this->assertEquals($pivot_invoice->partial, 0); $this->assertEquals($pivot_invoice->amount, 10.0000); $this->assertEquals($pivot_invoice->balance, 8.0000); + } } @@ -364,7 +380,7 @@ class PaymentTest extends TestCase 'amount' => 6.0 ], ], - 'payment_date' => '2019/12/12', + 'date' => '2019/12/12', ]; $response = $this->withHeaders([ @@ -425,7 +441,7 @@ class PaymentTest extends TestCase 'amount' => 2.0 ], ], - 'payment_date' => '2019/12/12', + 'date' => '2019/12/12', ]; $response = $this->withHeaders([ diff --git a/tests/MockAccountData.php b/tests/MockAccountData.php index f202b702a6b6..e8b6d1c16d3c 100644 --- a/tests/MockAccountData.php +++ b/tests/MockAccountData.php @@ -17,16 +17,19 @@ use App\DataMapper\DefaultSettings; use App\Factory\ClientFactory; use App\Factory\CompanyUserFactory; use App\Factory\InvoiceFactory; +use App\Factory\InvoiceInvitationFactory; use App\Factory\InvoiceItemFactory; use App\Factory\InvoiceToRecurringInvoiceFactory; use App\Helpers\Invoice\InvoiceSum; use App\Jobs\Company\UpdateCompanyLedgerWithInvoice; +use App\Jobs\Invoice\CreateInvoiceInvitations; use App\Models\Client; use App\Models\CompanyGateway; use App\Models\CompanyToken; use App\Models\Credit; use App\Models\GroupSetting; use App\Models\Invoice; +use App\Models\InvoiceInvitation; use App\Models\Quote; use App\Models\RecurringInvoice; use App\Models\User; @@ -127,13 +130,33 @@ trait MockAccountData // 'settings' => json_encode(DefaultSettings::userSettings()), // ]); - $this->client = ClientFactory::create($this->company->id, $this->user->id); - $this->client->save(); + $this->client = ClientFactory::create($this->company->id, $this->user->id); + $this->client->save(); + factory(\App\Models\ClientContact::class,1)->create([ + 'user_id' => $this->user->id, + 'client_id' => $this->client->id, + 'company_id' => $this->company->id, + 'is_primary' => 1, + 'send_invoice' => true, + ]); + + factory(\App\Models\ClientContact::class,1)->create([ + 'user_id' => $this->user->id, + 'client_id' => $this->client->id, + 'company_id' => $this->company->id, + 'send_invoice' => true + ]); + + $gs = new GroupSetting; $gs->name = 'Test'; $gs->company_id = $this->client->company_id; $gs->settings = ClientSettings::buildClientSettings($this->company->settings, $this->client->settings); + + $gs_settings = $gs->settings; + $gs_settings->website = 'http://staging.invoicing.co'; + $gs->settings = $gs_settings; $gs->save(); $this->client->group_settings_id = $gs->id; @@ -154,6 +177,29 @@ trait MockAccountData $this->invoice->save(); + $this->invoice->markSent(); + + $contacts = $this->invoice->client->contacts; + + $contacts->each(function ($contact) { + + $invitation = InvoiceInvitation::whereCompanyId($this->invoice->company_id) + ->whereClientContactId($contact->id) + ->whereInvoiceId($this->invoice->id) + ->first(); + + if(!$invitation && $contact->send_invoice) { + $ii = InvoiceInvitationFactory::create($this->invoice->company_id, $this->invoice->user_id); + $ii->invoice_id = $this->invoice->id; + $ii->client_contact_id = $contact->id; + $ii->save(); + } + else if($invitation && !$contact->send_invoice) { + $invitation->delete(); + } + + }); + UpdateCompanyLedgerWithInvoice::dispatchNow($this->invoice, $this->invoice->amount); $recurring_invoice = InvoiceToRecurringInvoiceFactory::create($this->invoice); |