mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-12-08 02:15:32 -05:00
Update Purchase Order Scaffold
This commit is contained in:
commit
4a2ecdb6a7
@ -1 +1 @@
|
|||||||
5.3.93
|
5.3.96
|
||||||
@ -103,7 +103,7 @@ class Kernel extends ConsoleKernel
|
|||||||
|
|
||||||
if (config('queue.default') == 'database' && Ninja::isSelfHost() && config('ninja.internal_queue_enabled') && !config('ninja.is_docker')) {
|
if (config('queue.default') == 'database' && Ninja::isSelfHost() && config('ninja.internal_queue_enabled') && !config('ninja.is_docker')) {
|
||||||
|
|
||||||
$schedule->command('queue:work database --stop-when-empty')->everyMinute()->withoutOverlapping();
|
$schedule->command('queue:work database --stop-when-empty --memory=256')->everyMinute()->withoutOverlapping();
|
||||||
|
|
||||||
$schedule->command('queue:restart')->everyFiveMinutes()->withoutOverlapping();
|
$schedule->command('queue:restart')->everyFiveMinutes()->withoutOverlapping();
|
||||||
|
|
||||||
|
|||||||
@ -98,11 +98,11 @@ class CompanySettings extends BaseSettings
|
|||||||
public $expense_number_pattern = ''; //@implemented
|
public $expense_number_pattern = ''; //@implemented
|
||||||
public $expense_number_counter = 1; //@implemented
|
public $expense_number_counter = 1; //@implemented
|
||||||
|
|
||||||
public $recurring_expense_number_pattern = '';
|
public $recurring_expense_number_pattern = '';
|
||||||
public $recurring_expense_number_counter = 1;
|
public $recurring_expense_number_counter = 1;
|
||||||
|
|
||||||
public $recurring_quote_number_pattern = '';
|
public $recurring_quote_number_pattern = '';
|
||||||
public $recurring_quote_number_counter = 1;
|
public $recurring_quote_number_counter = 1;
|
||||||
|
|
||||||
public $vendor_number_pattern = ''; //@implemented
|
public $vendor_number_pattern = ''; //@implemented
|
||||||
public $vendor_number_counter = 1; //@implemented
|
public $vendor_number_counter = 1; //@implemented
|
||||||
@ -279,6 +279,9 @@ class CompanySettings extends BaseSettings
|
|||||||
public $email_from_name = '';
|
public $email_from_name = '';
|
||||||
public $auto_archive_invoice_cancelled = false;
|
public $auto_archive_invoice_cancelled = false;
|
||||||
|
|
||||||
|
|
||||||
|
public $purchase_order_number_counter = 1; //TODO
|
||||||
|
|
||||||
public static $casts = [
|
public static $casts = [
|
||||||
'purchase_order_number_pattern' => 'purchase_order_number_pattern',
|
'purchase_order_number_pattern' => 'purchase_order_number_pattern',
|
||||||
'purchase_order_number_counter' => 'int',
|
'purchase_order_number_counter' => 'int',
|
||||||
@ -479,6 +482,7 @@ class CompanySettings extends BaseSettings
|
|||||||
'portal_custom_footer' => 'string',
|
'portal_custom_footer' => 'string',
|
||||||
'portal_custom_js' => 'string',
|
'portal_custom_js' => 'string',
|
||||||
'client_portal_enable_uploads' => 'bool',
|
'client_portal_enable_uploads' => 'bool',
|
||||||
|
'purchase_order_number_counter' => 'integer',
|
||||||
];
|
];
|
||||||
|
|
||||||
public static $free_plan_casts = [
|
public static $free_plan_casts = [
|
||||||
|
|||||||
38
app/Events/PurchaseOrder/PurchaseOrderWasMarkedSent.php
Normal file
38
app/Events/PurchaseOrder/PurchaseOrderWasMarkedSent.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace App\Events\PurchaseOrder;
|
||||||
|
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\PurchaseOrder;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class PurchaseOrderWasMarkedSent.
|
||||||
|
*/
|
||||||
|
class PurchaseOrderWasMarkedSent
|
||||||
|
{
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \App\Models\PurchaseOrder
|
||||||
|
*/
|
||||||
|
public $purchase_order;
|
||||||
|
|
||||||
|
public $company;
|
||||||
|
|
||||||
|
public $event_vars;
|
||||||
|
/**
|
||||||
|
* Create a new event instance.
|
||||||
|
*
|
||||||
|
* @param PurchaseOrder $purchase_order
|
||||||
|
* @param Company $company
|
||||||
|
* @param array $event_vars
|
||||||
|
*/
|
||||||
|
public function __construct(PurchaseOrder $purchase_order, Company $company, array $event_vars)
|
||||||
|
{
|
||||||
|
$this->purchase_order = $purchase_order;
|
||||||
|
$this->company = $company;
|
||||||
|
$this->event_vars = $event_vars;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -78,7 +78,7 @@ class QuoteItemExport extends BaseExport
|
|||||||
'tax_name2' => 'item.tax_name2',
|
'tax_name2' => 'item.tax_name2',
|
||||||
'tax_name3' => 'item.tax_name3',
|
'tax_name3' => 'item.tax_name3',
|
||||||
'line_total' => 'item.line_total',
|
'line_total' => 'item.line_total',
|
||||||
'gross_line_total' => 'item.gross_line_total',
|
// 'gross_line_total' => 'item.gross_line_total',
|
||||||
'custom_value1' => 'item.custom_value1',
|
'custom_value1' => 'item.custom_value1',
|
||||||
'custom_value2' => 'item.custom_value2',
|
'custom_value2' => 'item.custom_value2',
|
||||||
'custom_value3' => 'item.custom_value3',
|
'custom_value3' => 'item.custom_value3',
|
||||||
|
|||||||
31
app/Factory/PurchaseOrderInvitationFactory.php
Normal file
31
app/Factory/PurchaseOrderInvitationFactory.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace App\Factory;
|
||||||
|
|
||||||
|
use App\Models\PurchaseOrderInvitation;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class PurchaseOrderInvitationFactory
|
||||||
|
{
|
||||||
|
public static function create(int $company_id, int $user_id) :PurchaseOrderInvitation
|
||||||
|
{
|
||||||
|
$ci = new PurchaseOrderInvitation();
|
||||||
|
$ci->company_id = $company_id;
|
||||||
|
$ci->user_id = $user_id;
|
||||||
|
$ci->vendor_contact_id = null;
|
||||||
|
$ci->purchase_order_id = null;
|
||||||
|
$ci->key = Str::random(config('ninja.key_length'));
|
||||||
|
$ci->transaction_reference = null;
|
||||||
|
$ci->message_id = null;
|
||||||
|
$ci->email_error = '';
|
||||||
|
$ci->signature_base64 = '';
|
||||||
|
$ci->signature_date = null;
|
||||||
|
$ci->sent_date = null;
|
||||||
|
$ci->viewed_date = null;
|
||||||
|
$ci->opened_date = null;
|
||||||
|
|
||||||
|
return $ci;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -48,6 +48,7 @@ class RecurringInvoiceFactory
|
|||||||
$invoice->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
|
$invoice->frequency_id = RecurringInvoice::FREQUENCY_MONTHLY;
|
||||||
$invoice->last_sent_date = null;
|
$invoice->last_sent_date = null;
|
||||||
$invoice->next_send_date = null;
|
$invoice->next_send_date = null;
|
||||||
|
$invoice->next_send_date_client = null;
|
||||||
$invoice->remaining_cycles = -1;
|
$invoice->remaining_cycles = -1;
|
||||||
$invoice->paid_to_date = 0;
|
$invoice->paid_to_date = 0;
|
||||||
$invoice->auto_bill_enabled = false;
|
$invoice->auto_bill_enabled = false;
|
||||||
|
|||||||
@ -22,6 +22,7 @@ use App\Models\ClientContact;
|
|||||||
use App\Models\CreditInvitation;
|
use App\Models\CreditInvitation;
|
||||||
use App\Models\InvoiceInvitation;
|
use App\Models\InvoiceInvitation;
|
||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
|
use App\Models\PurchaseOrderInvitation;
|
||||||
use App\Models\QuoteInvitation;
|
use App\Models\QuoteInvitation;
|
||||||
use App\Services\ClientPortal\InstantPayment;
|
use App\Services\ClientPortal\InstantPayment;
|
||||||
use App\Utils\CurlUtils;
|
use App\Utils\CurlUtils;
|
||||||
@ -41,7 +42,7 @@ class InvitationController extends Controller
|
|||||||
use MakesDates;
|
use MakesDates;
|
||||||
|
|
||||||
public function router(string $entity, string $invitation_key)
|
public function router(string $entity, string $invitation_key)
|
||||||
{
|
{
|
||||||
Auth::logout();
|
Auth::logout();
|
||||||
|
|
||||||
return $this->genericRouter($entity, $invitation_key);
|
return $this->genericRouter($entity, $invitation_key);
|
||||||
@ -166,7 +167,7 @@ class InvitationController extends Controller
|
|||||||
{
|
{
|
||||||
|
|
||||||
set_time_limit(45);
|
set_time_limit(45);
|
||||||
|
|
||||||
if(Ninja::isHosted())
|
if(Ninja::isHosted())
|
||||||
return $this->returnRawPdf($entity, $invitation_key);
|
return $this->returnRawPdf($entity, $invitation_key);
|
||||||
|
|
||||||
@ -202,7 +203,7 @@ class InvitationController extends Controller
|
|||||||
return response()->streamDownload(function () use($file) {
|
return response()->streamDownload(function () use($file) {
|
||||||
echo $file;
|
echo $file;
|
||||||
}, $file_name, $headers);
|
}, $file_name, $headers);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function routerForIframe(string $entity, string $client_hash, string $invitation_key)
|
public function routerForIframe(string $entity, string $client_hash, string $invitation_key)
|
||||||
@ -228,14 +229,14 @@ class InvitationController extends Controller
|
|||||||
$invitation = InvoiceInvitation::where('key', $invitation_key)
|
$invitation = InvoiceInvitation::where('key', $invitation_key)
|
||||||
->with('contact.client')
|
->with('contact.client')
|
||||||
->firstOrFail();
|
->firstOrFail();
|
||||||
|
|
||||||
auth()->guard('contact')->loginUsingId($invitation->contact->id, true);
|
auth()->guard('contact')->loginUsingId($invitation->contact->id, true);
|
||||||
|
|
||||||
$invoice = $invitation->invoice;
|
$invoice = $invitation->invoice;
|
||||||
|
|
||||||
if($invoice->partial > 0)
|
if($invoice->partial > 0)
|
||||||
$amount = round($invoice->partial, (int)$invoice->client->currency()->precision);
|
$amount = round($invoice->partial, (int)$invoice->client->currency()->precision);
|
||||||
else
|
else
|
||||||
$amount = round($invoice->balance, (int)$invoice->client->currency()->precision);
|
$amount = round($invoice->balance, (int)$invoice->client->currency()->precision);
|
||||||
|
|
||||||
$gateways = $invitation->contact->client->service()->getPaymentMethods($amount);
|
$gateways = $invitation->contact->client->service()->getPaymentMethods($amount);
|
||||||
@ -279,6 +280,10 @@ class InvitationController extends Controller
|
|||||||
$invite = CreditInvitation::withTrashed()->where('key', $invitation_key)->first();
|
$invite = CreditInvitation::withTrashed()->where('key', $invitation_key)->first();
|
||||||
$invite->contact->send_email = false;
|
$invite->contact->send_email = false;
|
||||||
$invite->contact->save();
|
$invite->contact->save();
|
||||||
|
}elseif($entity == 'purchase_order'){
|
||||||
|
$invite = PurchaseOrderInvitation::withTrashed()->where('key', $invitation_key)->first();
|
||||||
|
$invite->contact->send_email = false;
|
||||||
|
$invite->contact->save();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return abort(404);
|
return abort(404);
|
||||||
|
|||||||
@ -204,9 +204,9 @@ class RecurringInvoiceController extends BaseController
|
|||||||
{
|
{
|
||||||
$recurring_invoice = $this->recurring_invoice_repo->save($request->all(), RecurringInvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id));
|
$recurring_invoice = $this->recurring_invoice_repo->save($request->all(), RecurringInvoiceFactory::create(auth()->user()->company()->id, auth()->user()->id));
|
||||||
|
|
||||||
$offset = $recurring_invoice->client->timezone_offset();
|
// $offset = $recurring_invoice->client->timezone_offset();
|
||||||
$recurring_invoice->next_send_date = Carbon::parse($recurring_invoice->next_send_date)->startOfDay()->addSeconds($offset);
|
// $recurring_invoice->next_send_date = Carbon::parse($recurring_invoice->next_send_date)->startOfDay()->addSeconds($offset);
|
||||||
$recurring_invoice->saveQuietly();
|
// $recurring_invoice->saveQuietly();
|
||||||
|
|
||||||
$recurring_invoice->service()
|
$recurring_invoice->service()
|
||||||
->triggeredActions($request)
|
->triggeredActions($request)
|
||||||
|
|||||||
@ -16,6 +16,7 @@ use App\Models\Client;
|
|||||||
use App\Utils\Ninja;
|
use App\Utils\Ninja;
|
||||||
use App\Utils\Traits\AppSetup;
|
use App\Utils\Traits\AppSetup;
|
||||||
use App\Utils\Traits\ClientGroupSettingsSaver;
|
use App\Utils\Traits\ClientGroupSettingsSaver;
|
||||||
|
use Beganovich\Snappdf\Snappdf;
|
||||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||||
use Illuminate\Support\Facades\Artisan;
|
use Illuminate\Support\Facades\Artisan;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
@ -134,21 +135,30 @@ class SelfUpdateController extends BaseController
|
|||||||
|
|
||||||
nlog("Extracting zip");
|
nlog("Extracting zip");
|
||||||
|
|
||||||
// $zipFile = new \PhpZip\ZipFile();
|
// try{
|
||||||
|
// $s = new Snappdf;
|
||||||
|
// $s->getChromiumPath();
|
||||||
|
// chmod($this->generatePlatformExecutable($s->getChromiumPath()), 0755);
|
||||||
|
// }
|
||||||
|
// catch(\Exception $e){
|
||||||
|
// nlog("I could not set the file permissions for chrome");
|
||||||
|
// }
|
||||||
|
|
||||||
// $zipFile->openFile($file);
|
$zipFile = new \PhpZip\ZipFile();
|
||||||
|
|
||||||
// $zipFile->extractTo(base_path());
|
$zipFile->openFile($file);
|
||||||
|
|
||||||
// $zipFile->close();
|
$zipFile->extractTo(base_path());
|
||||||
|
|
||||||
$zip = new \ZipArchive;
|
$zipFile->close();
|
||||||
|
|
||||||
|
// $zip = new \ZipArchive;
|
||||||
|
|
||||||
$res = $zip->open($file);
|
// $res = $zip->open($file);
|
||||||
if ($res === TRUE) {
|
// if ($res === TRUE) {
|
||||||
$zip->extractTo(base_path());
|
// $zip->extractTo(base_path());
|
||||||
$zip->close();
|
// $zip->close();
|
||||||
}
|
// }
|
||||||
|
|
||||||
nlog("Finished extracting files");
|
nlog("Finished extracting files");
|
||||||
|
|
||||||
|
|||||||
@ -55,6 +55,10 @@ class StoreRecurringExpenseRequest extends Request
|
|||||||
|
|
||||||
$input = $this->decodePrimaryKeys($input);
|
$input = $this->decodePrimaryKeys($input);
|
||||||
|
|
||||||
|
if (array_key_exists('next_send_date', $input) && is_string($input['next_send_date'])) {
|
||||||
|
$input['next_send_date_client'] = $input['next_send_date'];
|
||||||
|
}
|
||||||
|
|
||||||
if (array_key_exists('category_id', $input) && is_string($input['category_id'])) {
|
if (array_key_exists('category_id', $input) && is_string($input['category_id'])) {
|
||||||
$input['category_id'] = $this->decodePrimaryKey($input['category_id']);
|
$input['category_id'] = $this->decodePrimaryKey($input['category_id']);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -66,6 +66,10 @@ class UpdateRecurringExpenseRequest extends Request
|
|||||||
|
|
||||||
$input = $this->decodePrimaryKeys($input);
|
$input = $this->decodePrimaryKeys($input);
|
||||||
|
|
||||||
|
if (array_key_exists('next_send_date', $input) && is_string($input['next_send_date'])) {
|
||||||
|
$input['next_send_date_client'] = $input['next_send_date'];
|
||||||
|
}
|
||||||
|
|
||||||
if (array_key_exists('category_id', $input) && is_string($input['category_id'])) {
|
if (array_key_exists('category_id', $input) && is_string($input['category_id'])) {
|
||||||
$input['category_id'] = $this->decodePrimaryKey($input['category_id']);
|
$input['category_id'] = $this->decodePrimaryKey($input['category_id']);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,6 +67,10 @@ class StoreRecurringInvoiceRequest extends Request
|
|||||||
{
|
{
|
||||||
$input = $this->all();
|
$input = $this->all();
|
||||||
|
|
||||||
|
if (array_key_exists('next_send_date', $input) && is_string($input['next_send_date'])) {
|
||||||
|
$input['next_send_date_client'] = $input['next_send_date'];
|
||||||
|
}
|
||||||
|
|
||||||
if (array_key_exists('design_id', $input) && is_string($input['design_id'])) {
|
if (array_key_exists('design_id', $input) && is_string($input['design_id'])) {
|
||||||
$input['design_id'] = $this->decodePrimaryKey($input['design_id']);
|
$input['design_id'] = $this->decodePrimaryKey($input['design_id']);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -61,6 +61,10 @@ class UpdateRecurringInvoiceRequest extends Request
|
|||||||
{
|
{
|
||||||
$input = $this->all();
|
$input = $this->all();
|
||||||
|
|
||||||
|
if (array_key_exists('next_send_date', $input) && is_string($input['next_send_date'])) {
|
||||||
|
$input['next_send_date_client'] = $input['next_send_date'];
|
||||||
|
}
|
||||||
|
|
||||||
if (array_key_exists('design_id', $input) && is_string($input['design_id'])) {
|
if (array_key_exists('design_id', $input) && is_string($input['design_id'])) {
|
||||||
$input['design_id'] = $this->decodePrimaryKey($input['design_id']);
|
$input['design_id'] = $this->decodePrimaryKey($input['design_id']);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -562,7 +562,7 @@ class BaseImport
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function finalizeImport()
|
public function finalizeImport()
|
||||||
{
|
{
|
||||||
$data = [
|
$data = [
|
||||||
'errors' => $this->error_array,
|
'errors' => $this->error_array,
|
||||||
|
|||||||
@ -60,10 +60,7 @@ class Csv extends BaseImport implements ImportInterface
|
|||||||
) {
|
) {
|
||||||
$this->{$entity}();
|
$this->{$entity}();
|
||||||
}
|
}
|
||||||
|
|
||||||
//collate any errors
|
|
||||||
|
|
||||||
$this->finalizeImport();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function client()
|
public function client()
|
||||||
|
|||||||
@ -41,7 +41,7 @@ class Freshbooks extends BaseImport
|
|||||||
|
|
||||||
//collate any errors
|
//collate any errors
|
||||||
|
|
||||||
$this->finalizeImport();
|
// $this->finalizeImport();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function client()
|
public function client()
|
||||||
|
|||||||
@ -39,7 +39,7 @@ class Invoice2Go extends BaseImport
|
|||||||
|
|
||||||
//collate any errors
|
//collate any errors
|
||||||
|
|
||||||
$this->finalizeImport();
|
// $this->finalizeImport();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -38,7 +38,7 @@ class Invoicely extends BaseImport
|
|||||||
|
|
||||||
//collate any errors
|
//collate any errors
|
||||||
|
|
||||||
$this->finalizeImport();
|
// $this->finalizeImport();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function client()
|
public function client()
|
||||||
|
|||||||
@ -54,7 +54,7 @@ class Wave extends BaseImport implements ImportInterface
|
|||||||
|
|
||||||
//collate any errors
|
//collate any errors
|
||||||
|
|
||||||
$this->finalizeImport();
|
// $this->finalizeImport();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function client()
|
public function client()
|
||||||
|
|||||||
@ -40,7 +40,7 @@ class Zoho extends BaseImport
|
|||||||
|
|
||||||
//collate any errors
|
//collate any errors
|
||||||
|
|
||||||
$this->finalizeImport();
|
// $this->finalizeImport();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function client()
|
public function client()
|
||||||
|
|||||||
@ -94,6 +94,8 @@ class RecurringExpensesCron
|
|||||||
$expense->save();
|
$expense->save();
|
||||||
|
|
||||||
$recurring_expense->next_send_date = $recurring_expense->nextSendDate();
|
$recurring_expense->next_send_date = $recurring_expense->nextSendDate();
|
||||||
|
$recurring_expense->next_send_date_client = $recurring_expense->next_send_date;
|
||||||
|
|
||||||
$recurring_expense->remaining_cycles = $recurring_expense->remainingCycles();
|
$recurring_expense->remaining_cycles = $recurring_expense->remainingCycles();
|
||||||
$recurring_expense->save();
|
$recurring_expense->save();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,6 +79,8 @@ class CSVIngest implements ShouldQueue {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$engine->finalizeImport();
|
||||||
|
|
||||||
$this->checkContacts();
|
$this->checkContacts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
95
app/Jobs/Inventory/AdjustProductInventory.php
Normal file
95
app/Jobs/Inventory/AdjustProductInventory.php
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Jobs\Inventory;
|
||||||
|
|
||||||
|
use App\Libraries\MultiDB;
|
||||||
|
use App\Models\Company;
|
||||||
|
use App\Models\Invoice;
|
||||||
|
use App\Models\Product;
|
||||||
|
use App\Utils\Traits\NumberFormatter;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
//todo - ensure we are MultiDB Aware in dispatched jobs
|
||||||
|
|
||||||
|
class AdjustProductInventory implements ShouldQueue
|
||||||
|
{
|
||||||
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||||
|
|
||||||
|
public Company $company;
|
||||||
|
|
||||||
|
public Invoice $invoice;
|
||||||
|
|
||||||
|
public array $old_invoice;
|
||||||
|
|
||||||
|
public function __construct(Company $company, Invoice $invoice, array $old_invoice = [])
|
||||||
|
{
|
||||||
|
|
||||||
|
$this->company = $company;
|
||||||
|
$this->invoice = $invoice;
|
||||||
|
$this->old_invoice = $old_invoice;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute the job.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
public function handle()
|
||||||
|
{
|
||||||
|
MultiDB::setDb($this->company->db);
|
||||||
|
|
||||||
|
if(count($this->old_invoice) > 0)
|
||||||
|
return $this->existingInventoryAdjustment();
|
||||||
|
|
||||||
|
return $this->newInventoryAdjustment();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newInventoryAdjustment()
|
||||||
|
{
|
||||||
|
|
||||||
|
$line_items = $this->invoice->line_items;
|
||||||
|
|
||||||
|
foreach($line_items as $item)
|
||||||
|
{
|
||||||
|
|
||||||
|
$p = Product::where('product_key', $item->product_key)->where('company_id', $this->company->id)->where('in_stock_quantity', '>', 0)->first();
|
||||||
|
$p->in_stock_quantity -= $item->quantity;
|
||||||
|
$p->save();
|
||||||
|
|
||||||
|
//check threshols and notify user
|
||||||
|
|
||||||
|
if($p->stock_notification_threshold && $p->in_stock_quantity <= $p->stock_notification_threshold)
|
||||||
|
$this->notifyStockLevels($p, 'product');
|
||||||
|
elseif($this->company->stock_notification_threshold && $p->in_stock_quantity <= $this->company->stock_notification_threshold){
|
||||||
|
$this->notifyStocklevels($p, 'company');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function existingInventoryAdjustment()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function notifyStocklevels(Product $product, string $notification_level)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -105,6 +105,7 @@ class SendRecurring implements ShouldQueue
|
|||||||
nlog("updating recurring invoice dates");
|
nlog("updating recurring invoice dates");
|
||||||
/* Set next date here to prevent a recurring loop forming */
|
/* Set next date here to prevent a recurring loop forming */
|
||||||
$this->recurring_invoice->next_send_date = $this->recurring_invoice->nextSendDate();
|
$this->recurring_invoice->next_send_date = $this->recurring_invoice->nextSendDate();
|
||||||
|
$this->recurring_invoice->next_send_date_client = $this->recurring_invoice->nextSendDateClient();
|
||||||
$this->recurring_invoice->remaining_cycles = $this->recurring_invoice->remainingCycles();
|
$this->recurring_invoice->remaining_cycles = $this->recurring_invoice->remainingCycles();
|
||||||
$this->recurring_invoice->last_sent_date = now();
|
$this->recurring_invoice->last_sent_date = now();
|
||||||
|
|
||||||
|
|||||||
@ -666,7 +666,7 @@ class Client extends BaseModel implements HasLocalePreference
|
|||||||
|
|
||||||
$offset -= $timezone->utc_offset;
|
$offset -= $timezone->utc_offset;
|
||||||
$offset += ($entity_send_time * 3600);
|
$offset += ($entity_send_time * 3600);
|
||||||
|
|
||||||
return $offset;
|
return $offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -103,6 +103,10 @@ class Company extends BaseModel
|
|||||||
'markdown_email_enabled',
|
'markdown_email_enabled',
|
||||||
'stop_on_unpaid_recurring',
|
'stop_on_unpaid_recurring',
|
||||||
'use_quote_terms_on_conversion',
|
'use_quote_terms_on_conversion',
|
||||||
|
'enable_applying_payments',
|
||||||
|
'track_inventory',
|
||||||
|
'inventory_notification_threshold',
|
||||||
|
'stock_notification'
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $hidden = [
|
protected $hidden = [
|
||||||
|
|||||||
@ -36,6 +36,9 @@ class Product extends BaseModel
|
|||||||
'tax_rate1',
|
'tax_rate1',
|
||||||
'tax_rate2',
|
'tax_rate2',
|
||||||
'tax_rate3',
|
'tax_rate3',
|
||||||
|
'in_stock_quantity',
|
||||||
|
'stock_notification_threshold',
|
||||||
|
'stock_notification',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $touches = [];
|
protected $touches = [];
|
||||||
|
|||||||
@ -12,10 +12,12 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
|
||||||
use App\Helpers\Invoice\InvoiceSum;
|
use App\Jobs\Entity\CreateEntityPdf;
|
||||||
use App\Helpers\Invoice\InvoiceSumInclusive;
|
|
||||||
use App\Services\PurchaseOrder\PurchaseOrderService;
|
use App\Services\PurchaseOrder\PurchaseOrderService;
|
||||||
|
use App\Utils\Ninja;
|
||||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\Support\Facades\Storage;
|
||||||
|
|
||||||
class PurchaseOrder extends BaseModel
|
class PurchaseOrder extends BaseModel
|
||||||
{
|
{
|
||||||
@ -94,9 +96,8 @@ class PurchaseOrder extends BaseModel
|
|||||||
|
|
||||||
const STATUS_DRAFT = 1;
|
const STATUS_DRAFT = 1;
|
||||||
const STATUS_SENT = 2;
|
const STATUS_SENT = 2;
|
||||||
const STATUS_APPROVED = 3;
|
const STATUS_PARTIAL = 3;
|
||||||
const STATUS_CONVERTED = 4;
|
const STATUS_APPLIED = 4;
|
||||||
const STATUS_EXPIRED = -1;
|
|
||||||
|
|
||||||
public function assigned_user()
|
public function assigned_user()
|
||||||
{
|
{
|
||||||
@ -132,10 +133,52 @@ class PurchaseOrder extends BaseModel
|
|||||||
{
|
{
|
||||||
return $this->belongsTo(Client::class)->withTrashed();
|
return $this->belongsTo(Client::class)->withTrashed();
|
||||||
}
|
}
|
||||||
|
public function markInvitationsSent()
|
||||||
|
{
|
||||||
|
$this->invitations->each(function ($invitation) {
|
||||||
|
if (! isset($invitation->sent_date)) {
|
||||||
|
$invitation->sent_date = Carbon::now();
|
||||||
|
$invitation->save();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function pdf_file_path($invitation = null, string $type = 'path', bool $portal = false)
|
||||||
|
{
|
||||||
|
if (! $invitation) {
|
||||||
|
|
||||||
|
if($this->invitations()->exists())
|
||||||
|
$invitation = $this->invitations()->first();
|
||||||
|
else{
|
||||||
|
$this->service()->createInvitations();
|
||||||
|
$invitation = $this->invitations()->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$invitation)
|
||||||
|
throw new \Exception('Hard fail, could not create an invitation - is there a valid contact?');
|
||||||
|
|
||||||
|
$file_path = $this->client->credit_filepath($invitation).$this->numberFormatter().'.pdf';
|
||||||
|
|
||||||
|
if(Ninja::isHosted() && $portal && Storage::disk(config('filesystems.default'))->exists($file_path)){
|
||||||
|
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
|
||||||
|
}
|
||||||
|
elseif(Ninja::isHosted() && $portal){
|
||||||
|
$file_path = CreateEntityPdf::dispatchNow($invitation,config('filesystems.default'));
|
||||||
|
return Storage::disk(config('filesystems.default'))->{$type}($file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Storage::disk('public')->exists($file_path))
|
||||||
|
return Storage::disk('public')->{$type}($file_path);
|
||||||
|
|
||||||
|
$file_path = CreateEntityPdf::dispatchNow($invitation);
|
||||||
|
return Storage::disk('public')->{$type}($file_path);
|
||||||
|
}
|
||||||
|
|
||||||
public function invitations()
|
public function invitations()
|
||||||
{
|
{
|
||||||
return $this->hasMany(CreditInvitation::class);
|
return $this->hasMany(PurchaseOrderInvitation::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function project()
|
public function project()
|
||||||
@ -168,17 +211,4 @@ class PurchaseOrder extends BaseModel
|
|||||||
return $this->morphMany(Document::class, 'documentable');
|
return $this->morphMany(Document::class, 'documentable');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function calc()
|
|
||||||
{
|
|
||||||
$credit_calc = null;
|
|
||||||
|
|
||||||
if ($this->uses_inclusive_taxes) {
|
|
||||||
$credit_calc = new InvoiceSumInclusive($this);
|
|
||||||
} else {
|
|
||||||
$credit_calc = new InvoiceSum($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $credit_calc->build();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
81
app/Models/PurchaseOrderInvitation.php
Normal file
81
app/Models/PurchaseOrderInvitation.php
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Utils\Traits\Inviteable;
|
||||||
|
use App\Utils\Traits\MakesDates;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
class PurchaseOrderInvitation extends BaseModel
|
||||||
|
{
|
||||||
|
use MakesDates;
|
||||||
|
use SoftDeletes;
|
||||||
|
use Inviteable;
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'id',
|
||||||
|
'vendor_contact_id',
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $with = [
|
||||||
|
'company',
|
||||||
|
'contact',
|
||||||
|
];
|
||||||
|
protected $touches = ['purchase_order'];
|
||||||
|
|
||||||
|
public function getEntityType()
|
||||||
|
{
|
||||||
|
return self::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function entityType()
|
||||||
|
{
|
||||||
|
return PurchaseOrder::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function purchase_order()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(PurchaseOrder::class)->withTrashed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function contact()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(VendorContact::class, 'vendor_contact_id', 'id')->withTrashed();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function user()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class)->withTrashed();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function company()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Company::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName()
|
||||||
|
{
|
||||||
|
return $this->key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function markViewed()
|
||||||
|
{
|
||||||
|
$this->viewed_date = Carbon::now();
|
||||||
|
$this->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -63,6 +63,7 @@ class RecurringExpense extends BaseModel
|
|||||||
'last_sent_date',
|
'last_sent_date',
|
||||||
'next_send_date',
|
'next_send_date',
|
||||||
'remaining_cycles',
|
'remaining_cycles',
|
||||||
|
'next_send_date_client',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
@ -153,6 +154,43 @@ class RecurringExpense extends BaseModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function nextSendDateClient() :?Carbon
|
||||||
|
{
|
||||||
|
if (!$this->next_send_date) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($this->frequency_id) {
|
||||||
|
case RecurringInvoice::FREQUENCY_DAILY:
|
||||||
|
return Carbon::parse($this->next_send_date)->startOfDay()->addDay();
|
||||||
|
case RecurringInvoice::FREQUENCY_WEEKLY:
|
||||||
|
return Carbon::parse($this->next_send_date)->startOfDay()->addWeek();
|
||||||
|
case RecurringInvoice::FREQUENCY_TWO_WEEKS:
|
||||||
|
return Carbon::parse($this->next_send_date)->startOfDay()->addWeeks(2);
|
||||||
|
case RecurringInvoice::FREQUENCY_FOUR_WEEKS:
|
||||||
|
return Carbon::parse($this->next_send_date)->startOfDay()->addWeeks(4);
|
||||||
|
case RecurringInvoice::FREQUENCY_MONTHLY:
|
||||||
|
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthNoOverflow();
|
||||||
|
case RecurringInvoice::FREQUENCY_TWO_MONTHS:
|
||||||
|
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(2);
|
||||||
|
case RecurringInvoice::FREQUENCY_THREE_MONTHS:
|
||||||
|
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(3);
|
||||||
|
case RecurringInvoice::FREQUENCY_FOUR_MONTHS:
|
||||||
|
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(4);
|
||||||
|
case RecurringInvoice::FREQUENCY_SIX_MONTHS:
|
||||||
|
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(6);
|
||||||
|
case RecurringInvoice::FREQUENCY_ANNUALLY:
|
||||||
|
return Carbon::parse($this->next_send_date)->startOfDay()->addYear();
|
||||||
|
case RecurringInvoice::FREQUENCY_TWO_YEARS:
|
||||||
|
return Carbon::parse($this->next_send_date)->startOfDay()->addYears(2);
|
||||||
|
case RecurringInvoice::FREQUENCY_THREE_YEARS:
|
||||||
|
return Carbon::parse($this->next_send_date)->startOfDay()->addYears(3);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function remainingCycles() : int
|
public function remainingCycles() : int
|
||||||
{
|
{
|
||||||
if ($this->remaining_cycles == 0) {
|
if ($this->remaining_cycles == 0) {
|
||||||
|
|||||||
@ -108,6 +108,7 @@ class RecurringInvoice extends BaseModel
|
|||||||
'assigned_user_id',
|
'assigned_user_id',
|
||||||
'exchange_rate',
|
'exchange_rate',
|
||||||
'vendor_id',
|
'vendor_id',
|
||||||
|
'next_send_date_client',
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
@ -224,7 +225,7 @@ class RecurringInvoice extends BaseModel
|
|||||||
|
|
||||||
public function nextSendDate() :?Carbon
|
public function nextSendDate() :?Carbon
|
||||||
{
|
{
|
||||||
if (!$this->next_send_date) {
|
if (!$this->next_send_date_client) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,49 +237,93 @@ class RecurringInvoice extends BaseModel
|
|||||||
|
|
||||||
/* Lets set the next send date to now so we increment from today, rather than in the past*/
|
/* Lets set the next send date to now so we increment from today, rather than in the past*/
|
||||||
if(Carbon::parse($this->next_send_date)->lt(now()->subDays(3)))
|
if(Carbon::parse($this->next_send_date)->lt(now()->subDays(3)))
|
||||||
$this->next_send_date = now()->format('Y-m-d');
|
$this->next_send_date_client = now()->format('Y-m-d');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
As we are firing at UTC+0 if our offset is negative it is technically firing the day before so we always need
|
As we are firing at UTC+0 if our offset is negative it is technically firing the day before so we always need
|
||||||
to add ON a day - a day = 86400 seconds
|
to add ON a day - a day = 86400 seconds
|
||||||
*/
|
*/
|
||||||
if($offset < 0)
|
// if($offset < 0)
|
||||||
$offset += 86400;
|
// $offset += 86400;
|
||||||
|
|
||||||
switch ($this->frequency_id) {
|
switch ($this->frequency_id) {
|
||||||
case self::FREQUENCY_DAILY:
|
case self::FREQUENCY_DAILY:
|
||||||
return Carbon::parse($this->next_send_date)->startOfDay()->addDay()->addSeconds($offset);
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addDay()->addSeconds($offset);
|
||||||
case self::FREQUENCY_WEEKLY:
|
case self::FREQUENCY_WEEKLY:
|
||||||
return Carbon::parse($this->next_send_date)->startOfDay()->addWeek()->addSeconds($offset);
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addWeek()->addSeconds($offset);
|
||||||
case self::FREQUENCY_TWO_WEEKS:
|
case self::FREQUENCY_TWO_WEEKS:
|
||||||
return Carbon::parse($this->next_send_date)->startOfDay()->addWeeks(2)->addSeconds($offset);
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addWeeks(2)->addSeconds($offset);
|
||||||
case self::FREQUENCY_FOUR_WEEKS:
|
case self::FREQUENCY_FOUR_WEEKS:
|
||||||
return Carbon::parse($this->next_send_date)->startOfDay()->addWeeks(4)->addSeconds($offset);
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addWeeks(4)->addSeconds($offset);
|
||||||
case self::FREQUENCY_MONTHLY:
|
case self::FREQUENCY_MONTHLY:
|
||||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthNoOverflow()->addSeconds($offset);
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthNoOverflow()->addSeconds($offset);
|
||||||
case self::FREQUENCY_TWO_MONTHS:
|
case self::FREQUENCY_TWO_MONTHS:
|
||||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(2)->addSeconds($offset);
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(2)->addSeconds($offset);
|
||||||
case self::FREQUENCY_THREE_MONTHS:
|
case self::FREQUENCY_THREE_MONTHS:
|
||||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(3)->addSeconds($offset);
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(3)->addSeconds($offset);
|
||||||
case self::FREQUENCY_FOUR_MONTHS:
|
case self::FREQUENCY_FOUR_MONTHS:
|
||||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(4)->addSeconds($offset);
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(4)->addSeconds($offset);
|
||||||
case self::FREQUENCY_SIX_MONTHS:
|
case self::FREQUENCY_SIX_MONTHS:
|
||||||
return Carbon::parse($this->next_send_date)->startOfDay()->addMonthsNoOverflow(6)->addSeconds($offset);
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(6)->addSeconds($offset);
|
||||||
case self::FREQUENCY_ANNUALLY:
|
case self::FREQUENCY_ANNUALLY:
|
||||||
return Carbon::parse($this->next_send_date)->startOfDay()->addYear()->addSeconds($offset);
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addYear()->addSeconds($offset);
|
||||||
case self::FREQUENCY_TWO_YEARS:
|
case self::FREQUENCY_TWO_YEARS:
|
||||||
return Carbon::parse($this->next_send_date)->startOfDay()->addYears(2)->addSeconds($offset);
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addYears(2)->addSeconds($offset);
|
||||||
case self::FREQUENCY_THREE_YEARS:
|
case self::FREQUENCY_THREE_YEARS:
|
||||||
return Carbon::parse($this->next_send_date)->startOfDay()->addYears(3)->addSeconds($offset);
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addYears(3)->addSeconds($offset);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function nextSendDateClient() :?Carbon
|
||||||
|
{
|
||||||
|
if (!$this->next_send_date_client) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If this setting is enabled, the recurring invoice may be set in the past */
|
||||||
|
|
||||||
|
if($this->company->stop_on_unpaid_recurring) {
|
||||||
|
|
||||||
|
/* Lets set the next send date to now so we increment from today, rather than in the past*/
|
||||||
|
if(Carbon::parse($this->next_send_date)->lt(now()->subDays(3)))
|
||||||
|
$this->next_send_date_client = now()->format('Y-m-d');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($this->frequency_id) {
|
||||||
|
case self::FREQUENCY_DAILY:
|
||||||
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addDay();
|
||||||
|
case self::FREQUENCY_WEEKLY:
|
||||||
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addWeek();
|
||||||
|
case self::FREQUENCY_TWO_WEEKS:
|
||||||
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addWeeks(2);
|
||||||
|
case self::FREQUENCY_FOUR_WEEKS:
|
||||||
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addWeeks(4);
|
||||||
|
case self::FREQUENCY_MONTHLY:
|
||||||
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthNoOverflow();
|
||||||
|
case self::FREQUENCY_TWO_MONTHS:
|
||||||
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(2);
|
||||||
|
case self::FREQUENCY_THREE_MONTHS:
|
||||||
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(3);
|
||||||
|
case self::FREQUENCY_FOUR_MONTHS:
|
||||||
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(4);
|
||||||
|
case self::FREQUENCY_SIX_MONTHS:
|
||||||
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(6);
|
||||||
|
case self::FREQUENCY_ANNUALLY:
|
||||||
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addYear();
|
||||||
|
case self::FREQUENCY_TWO_YEARS:
|
||||||
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addYears(2);
|
||||||
|
case self::FREQUENCY_THREE_YEARS:
|
||||||
|
return Carbon::parse($this->next_send_date_client)->startOfDay()->addYears(3);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function nextDateByFrequency($date)
|
public function nextDateByFrequency($date)
|
||||||
{
|
{
|
||||||
$offset = $this->client->timezone_offset();
|
$offset = $this->client->timezone_offset();
|
||||||
@ -463,11 +508,11 @@ class RecurringInvoice extends BaseModel
|
|||||||
|
|
||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
if (!Carbon::parse($this->next_send_date)) {
|
if (!Carbon::parse($this->next_send_date_client)) {
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
$next_send_date = Carbon::parse($this->next_send_date)->copy();
|
$next_send_date = Carbon::parse($this->next_send_date_client)->copy();
|
||||||
|
|
||||||
for ($x=0; $x<$iterations; $x++) {
|
for ($x=0; $x<$iterations; $x++) {
|
||||||
// we don't add the days... we calc the day of the month!!
|
// we don't add the days... we calc the day of the month!!
|
||||||
|
|||||||
@ -136,4 +136,8 @@ class VendorContact extends Authenticatable implements HasLocalePreference
|
|||||||
->withTrashed()
|
->withTrashed()
|
||||||
->where('id', $this->decodePrimaryKey($value))->firstOrFail();
|
->where('id', $this->decodePrimaryKey($value))->firstOrFail();
|
||||||
}
|
}
|
||||||
|
public function purchase_order_invitations(): \Illuminate\Database\Eloquent\Relations\HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(PurchaseOrderInvitation::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,11 +12,14 @@
|
|||||||
|
|
||||||
namespace App\PaymentDrivers\Authorize;
|
namespace App\PaymentDrivers\Authorize;
|
||||||
|
|
||||||
|
use App\Models\Invoice;
|
||||||
use App\PaymentDrivers\AuthorizePaymentDriver;
|
use App\PaymentDrivers\AuthorizePaymentDriver;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
use net\authorize\api\contract\v1\CreateTransactionRequest;
|
use net\authorize\api\contract\v1\CreateTransactionRequest;
|
||||||
use net\authorize\api\contract\v1\CustomerProfilePaymentType;
|
use net\authorize\api\contract\v1\CustomerProfilePaymentType;
|
||||||
use net\authorize\api\contract\v1\OrderType;
|
use net\authorize\api\contract\v1\OrderType;
|
||||||
use net\authorize\api\contract\v1\PaymentProfileType;
|
use net\authorize\api\contract\v1\PaymentProfileType;
|
||||||
|
use net\authorize\api\contract\v1\ExtendedAmountType;
|
||||||
use net\authorize\api\contract\v1\TransactionRequestType;
|
use net\authorize\api\contract\v1\TransactionRequestType;
|
||||||
use net\authorize\api\controller\CreateTransactionController;
|
use net\authorize\api\controller\CreateTransactionController;
|
||||||
|
|
||||||
@ -25,6 +28,8 @@ use net\authorize\api\controller\CreateTransactionController;
|
|||||||
*/
|
*/
|
||||||
class ChargePaymentProfile
|
class ChargePaymentProfile
|
||||||
{
|
{
|
||||||
|
use MakesHash;
|
||||||
|
|
||||||
public function __construct(AuthorizePaymentDriver $authorize)
|
public function __construct(AuthorizePaymentDriver $authorize)
|
||||||
{
|
{
|
||||||
$this->authorize = $authorize;
|
$this->authorize = $authorize;
|
||||||
@ -44,19 +49,40 @@ class ChargePaymentProfile
|
|||||||
$profileToCharge->setPaymentProfile($paymentProfile);
|
$profileToCharge->setPaymentProfile($paymentProfile);
|
||||||
|
|
||||||
$invoice_numbers = '';
|
$invoice_numbers = '';
|
||||||
|
$taxAmount = 0;
|
||||||
|
$invoiceTotal = 0;
|
||||||
|
$invoiceTaxes = 0;
|
||||||
|
|
||||||
if($this->authorize->payment_hash->data)
|
if($this->authorize->payment_hash->data) {
|
||||||
$invoice_numbers = collect($this->authorize->payment_hash->data->invoices)->pluck('invoice_number')->implode(',');
|
$invoice_numbers = collect($this->authorize->payment_hash->data->invoices)->pluck('invoice_number')->implode(",");
|
||||||
|
$invObj = Invoice::whereIn('id', $this->transformKeys(array_column($this->authorize->payment_hash->invoices(), 'invoice_id')))->withTrashed()->get();
|
||||||
|
|
||||||
|
$invoiceTotal = round($invObj->pluck('amount')->sum(), 2);
|
||||||
|
$invoiceTaxes = round($invObj->pluck('total_taxes')->sum(), 2);
|
||||||
|
|
||||||
|
if ($invoiceTotal != $amount) {
|
||||||
|
$taxRatio = $amount/$invoiceTotal;
|
||||||
|
$taxAmount = round($invoiceTaxes*$taxRatio, 2);
|
||||||
|
} else {
|
||||||
|
$taxAmount = $invoiceTaxes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$description = "Invoices: {$invoice_numbers} for {$amount} for client {$this->authorize->client->present()->name()}";
|
$description = "Invoices: {$invoice_numbers} for {$amount} for client {$this->authorize->client->present()->name()}";
|
||||||
|
|
||||||
$order = new OrderType();
|
$order = new OrderType();
|
||||||
$order->setInvoiceNumber(substr($invoice_numbers,0,19));
|
$order->setInvoiceNumber(substr($invoice_numbers,0,19));
|
||||||
$order->setDescription(substr($description,0,255));
|
$order->setDescription(substr($description,0,255));
|
||||||
|
|
||||||
|
$tax = new ExtendedAmountType();
|
||||||
|
$tax->setName('tax');
|
||||||
|
$tax->setAmount($taxAmount);
|
||||||
|
|
||||||
$transactionRequestType = new TransactionRequestType();
|
$transactionRequestType = new TransactionRequestType();
|
||||||
$transactionRequestType->setTransactionType('authCaptureTransaction');
|
$transactionRequestType->setTransactionType('authCaptureTransaction');
|
||||||
$transactionRequestType->setAmount($amount);
|
$transactionRequestType->setAmount($amount);
|
||||||
|
$transactionRequestType->setTax($tax);
|
||||||
|
$transactionRequestType->setTaxExempt(empty($taxAmount));
|
||||||
$transactionRequestType->setOrder($order);
|
$transactionRequestType->setOrder($order);
|
||||||
$transactionRequestType->setProfile($profileToCharge);
|
$transactionRequestType->setProfile($profileToCharge);
|
||||||
$transactionRequestType->setCurrencyCode($this->authorize->client->currency()->code);
|
$transactionRequestType->setCurrencyCode($this->authorize->client->currency()->code);
|
||||||
|
|||||||
@ -245,7 +245,7 @@ class GoCardlessPaymentDriver extends BaseDriver
|
|||||||
sleep(1);
|
sleep(1);
|
||||||
|
|
||||||
foreach ($request->events as $event) {
|
foreach ($request->events as $event) {
|
||||||
if ($event['action'] === 'confirmed' || $event['action'] === 'paid_out') {
|
if ($event['action'] === 'confirmed' || $event['action'] === 'paid_out' || $event['action'] === 'paid') {
|
||||||
|
|
||||||
nlog("Searching for transaction reference");
|
nlog("Searching for transaction reference");
|
||||||
|
|
||||||
|
|||||||
@ -60,6 +60,7 @@ use App\Events\Payment\PaymentWasRefunded;
|
|||||||
use App\Events\Payment\PaymentWasRestored;
|
use App\Events\Payment\PaymentWasRestored;
|
||||||
use App\Events\Payment\PaymentWasUpdated;
|
use App\Events\Payment\PaymentWasUpdated;
|
||||||
use App\Events\Payment\PaymentWasVoided;
|
use App\Events\Payment\PaymentWasVoided;
|
||||||
|
use App\Events\PurchaseOrder\PurchaseOrderWasMarkedSent;
|
||||||
use App\Events\Quote\QuoteWasApproved;
|
use App\Events\Quote\QuoteWasApproved;
|
||||||
use App\Events\Quote\QuoteWasArchived;
|
use App\Events\Quote\QuoteWasArchived;
|
||||||
use App\Events\Quote\QuoteWasCreated;
|
use App\Events\Quote\QuoteWasCreated;
|
||||||
@ -558,6 +559,8 @@ class EventServiceProvider extends ServiceProvider
|
|||||||
VendorWasUpdated::class => [
|
VendorWasUpdated::class => [
|
||||||
VendorUpdatedActivity::class,
|
VendorUpdatedActivity::class,
|
||||||
],
|
],
|
||||||
|
PurchaseOrderWasMarkedSent::class => [
|
||||||
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -11,10 +11,9 @@
|
|||||||
|
|
||||||
namespace App\Repositories;
|
namespace App\Repositories;
|
||||||
|
|
||||||
use App\Factory\PurchaseOrderFactory;
|
|
||||||
use App\Models\PurchaseOrder;
|
use App\Models\PurchaseOrder;
|
||||||
use App\Models\Vendor;
|
use App\Models\PurchaseOrderInvitation;
|
||||||
use App\Models\VendorContact;
|
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
|
||||||
class PurchaseOrderRepository extends BaseRepository
|
class PurchaseOrderRepository extends BaseRepository
|
||||||
@ -27,144 +26,14 @@ class PurchaseOrderRepository extends BaseRepository
|
|||||||
|
|
||||||
public function save(array $data, PurchaseOrder $purchase_order) : ?PurchaseOrder
|
public function save(array $data, PurchaseOrder $purchase_order) : ?PurchaseOrder
|
||||||
{
|
{
|
||||||
|
$purchase_order->fill($data);
|
||||||
if(array_key_exists('vendor_id', $data))
|
|
||||||
$purchase_order->vendor_id = $data['vendor_id'];
|
|
||||||
|
|
||||||
$vendor = Vendor::where('id', $purchase_order->vendor_id)->withTrashed()->firstOrFail();
|
|
||||||
|
|
||||||
$state = [];
|
|
||||||
|
|
||||||
$resource = class_basename($purchase_order); //ie Invoice
|
|
||||||
|
|
||||||
if (! $purchase_order->id) {
|
|
||||||
$company_defaults = $vendor->setCompanyDefaults($data, lcfirst($resource));
|
|
||||||
$purchase_order->uses_inclusive_taxes = $vendor->getSetting('inclusive_taxes');
|
|
||||||
$data = array_merge($company_defaults, $data);
|
|
||||||
}
|
|
||||||
|
|
||||||
$tmp_data = $data; //preserves the $data array
|
|
||||||
|
|
||||||
/* We need to unset some variable as we sometimes unguard the model */
|
|
||||||
if (isset($tmp_data['invitations']))
|
|
||||||
unset($tmp_data['invitations']);
|
|
||||||
|
|
||||||
if (isset($tmp_data['vendor_contacts']))
|
|
||||||
unset($tmp_data['vendor_contacts']);
|
|
||||||
|
|
||||||
$purchase_order->fill($tmp_data);
|
|
||||||
|
|
||||||
$purchase_order->custom_surcharge_tax1 = $vendor->company->custom_surcharge_taxes1;
|
|
||||||
$purchase_order->custom_surcharge_tax2 = $vendor->company->custom_surcharge_taxes2;
|
|
||||||
$purchase_order->custom_surcharge_tax3 = $vendor->company->custom_surcharge_taxes3;
|
|
||||||
$purchase_order->custom_surcharge_tax4 = $vendor->company->custom_surcharge_taxes4;
|
|
||||||
|
|
||||||
if(!$purchase_order->id)
|
|
||||||
$this->new_model = true;
|
|
||||||
|
|
||||||
$purchase_order->saveQuietly();
|
|
||||||
|
|
||||||
/* Save any documents */
|
|
||||||
if (array_key_exists('documents', $data))
|
|
||||||
$this->saveDocuments($data['documents'], $purchase_order);
|
|
||||||
|
|
||||||
if (array_key_exists('file', $data))
|
|
||||||
$this->saveDocuments($data['file'], $purchase_order);
|
|
||||||
|
|
||||||
/* If invitations are present we need to filter existing invitations with the new ones */
|
|
||||||
if (isset($data['invitations'])) {
|
|
||||||
$invitations = collect($data['invitations']);
|
|
||||||
|
|
||||||
/* Get array of Keys which have been removed from the invitations array and soft delete each invitation */
|
|
||||||
$purchase_order->invitations->pluck('key')->diff($invitations->pluck('key'))->each(function ($invitation) {
|
|
||||||
$invitation = PurchaseOrderInvitation::where('key', $invitation)->first();
|
|
||||||
|
|
||||||
if ($invitation)
|
|
||||||
$invitation->delete();
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
foreach ($data['invitations'] as $invitation) {
|
|
||||||
|
|
||||||
//if no invitations are present - create one.
|
|
||||||
if (! $this->getInvitation($invitation, $resource)) {
|
|
||||||
|
|
||||||
if (isset($invitation['id']))
|
|
||||||
unset($invitation['id']);
|
|
||||||
|
|
||||||
//make sure we are creating an invite for a contact who belongs to the client only!
|
|
||||||
$contact = VendorContact::find($invitation['vendor_contact_id']);
|
|
||||||
|
|
||||||
if ($contact && $purchase_order->client_id == $contact->client_id) {
|
|
||||||
|
|
||||||
$new_invitation = PurchaseOrderInvitation::withTrashed()
|
|
||||||
->where('vendor_contact_id', $contact->id)
|
|
||||||
->where('purchase_order_id', $purchase_order->id)
|
|
||||||
->first();
|
|
||||||
|
|
||||||
if ($new_invitation && $new_invitation->trashed()) {
|
|
||||||
|
|
||||||
$new_invitation->restore();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
$new_invitation = PurchaseOrderFactory::create($purchase_order->company_id, $purchase_order->user_id);
|
|
||||||
$new_invitation->purchase_order_id = $purchase_order->id;
|
|
||||||
$new_invitation->vendor_contact_id = $contact->id;
|
|
||||||
$new_invitation->key = $this->createDbHash($purchase_order->company->db);
|
|
||||||
$new_invitation->save();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If no invitations have been created, this is our fail safe to maintain state*/
|
|
||||||
if ($purchase_order->invitations()->count() == 0)
|
|
||||||
$purchase_order->service()->createInvitations();
|
|
||||||
|
|
||||||
/* Apply entity number */
|
|
||||||
$purchase_order = $purchase_order->service()->applyNumber()->save();
|
|
||||||
|
|
||||||
/* Handle attempts where the deposit is greater than the amount/balance of the invoice */
|
|
||||||
if((int)$purchase_order->balance != 0 && $purchase_order->partial > $purchase_order->amount)
|
|
||||||
$purchase_order->partial = min($purchase_order->amount, $purchase_order->balance);
|
|
||||||
|
|
||||||
$purchase_order = $purchase_order->calc()->getPurchaseOrder();
|
|
||||||
|
|
||||||
if (! $purchase_order->design_id)
|
|
||||||
$purchase_order->design_id = $this->decodePrimaryKey($client->getSetting('credit_design_id'));
|
|
||||||
|
|
||||||
if(array_key_exists('invoice_id', $data) && $data['invoice_id'])
|
|
||||||
$purchase_order->invoice_id = $data['invoice_id'];
|
|
||||||
|
|
||||||
if($this->new_model)
|
|
||||||
event('eloquent.created: App\Models\PurchaseOrder', $purchase_order);
|
|
||||||
else
|
|
||||||
event('eloquent.updated: App\Models\PurchaseOrder', $purchase_order);
|
|
||||||
|
|
||||||
|
|
||||||
$purchase_order->save();
|
$purchase_order->save();
|
||||||
|
|
||||||
return $purchase_order->fresh();
|
return $purchase_order;
|
||||||
|
|
||||||
|
|
||||||
// $purchase_order->fill($data);
|
|
||||||
// $purchase_order->save();
|
|
||||||
|
|
||||||
// return $purchase_order;
|
|
||||||
}
|
}
|
||||||
|
public function getInvitationByKey($key) :?PurchaseOrderInvitation
|
||||||
public function getInvitation($invitation, $resource)
|
|
||||||
{
|
{
|
||||||
// if (is_array($invitation) && ! array_key_exists('key', $invitation))
|
return PurchaseOrderInvitation::where('key', $key)->first();
|
||||||
// return false;
|
|
||||||
|
|
||||||
// $invitation = PurchaseOrderInvitation::where('key', $invitation['key'])->first();
|
|
||||||
|
|
||||||
return $invitation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ namespace App\Services\Invoice;
|
|||||||
|
|
||||||
use App\Events\Invoice\InvoiceWasArchived;
|
use App\Events\Invoice\InvoiceWasArchived;
|
||||||
use App\Jobs\Entity\CreateEntityPdf;
|
use App\Jobs\Entity\CreateEntityPdf;
|
||||||
|
use App\Jobs\Inventory\AdjustProductInventory;
|
||||||
use App\Jobs\Invoice\InvoiceWorkflowSettings;
|
use App\Jobs\Invoice\InvoiceWorkflowSettings;
|
||||||
use App\Jobs\Util\UnlinkFile;
|
use App\Jobs\Util\UnlinkFile;
|
||||||
use App\Libraries\Currency\Conversion\CurrencyApi;
|
use App\Libraries\Currency\Conversion\CurrencyApi;
|
||||||
@ -564,6 +565,14 @@ class InvoiceService
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function adjustInventory()
|
||||||
|
{
|
||||||
|
if($this->invoice->company->track_inventory)
|
||||||
|
AdjustProductInventory::dispatch($this->invoice->company, $this->invoice, null);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the invoice.
|
* Saves the invoice.
|
||||||
* @return Invoice object
|
* @return Invoice object
|
||||||
|
|||||||
@ -763,7 +763,7 @@ class Design extends BaseDesign
|
|||||||
} elseif (Str::startsWith($variable, '$custom_surcharge')) {
|
} elseif (Str::startsWith($variable, '$custom_surcharge')) {
|
||||||
$_variable = ltrim($variable, '$'); // $custom_surcharge1 -> custom_surcharge1
|
$_variable = ltrim($variable, '$'); // $custom_surcharge1 -> custom_surcharge1
|
||||||
|
|
||||||
$visible = $this->entity->{$_variable} > 0 || $this->entity->{$_variable} > '0';
|
$visible = $this->entity->{$_variable} != 0 || $this->entity->{$_variable} != '0';
|
||||||
|
|
||||||
$elements[1]['elements'][] = ['element' => 'div', 'elements' => [
|
$elements[1]['elements'][] = ['element' => 'div', 'elements' => [
|
||||||
['element' => 'span', 'content' => $variable . '_label', 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1) . '-label']],
|
['element' => 'span', 'content' => $variable . '_label', 'properties' => ['hidden' => !$visible, 'data-ref' => 'totals_table-' . substr($variable, 1) . '-label']],
|
||||||
|
|||||||
@ -1,18 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
|
||||||
* Invoice Ninja (https://invoiceninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Services\PurchaseOrder;
|
namespace App\Services\PurchaseOrder;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Models\Client;
|
||||||
|
use App\Models\Credit;
|
||||||
use App\Models\PurchaseOrder;
|
use App\Models\PurchaseOrder;
|
||||||
use App\Models\Vendor;
|
|
||||||
use App\Services\AbstractService;
|
use App\Services\AbstractService;
|
||||||
use App\Utils\Traits\GeneratesCounter;
|
use App\Utils\Traits\GeneratesCounter;
|
||||||
use Illuminate\Database\QueryException;
|
use Illuminate\Database\QueryException;
|
||||||
@ -21,15 +15,15 @@ class ApplyNumber extends AbstractService
|
|||||||
{
|
{
|
||||||
use GeneratesCounter;
|
use GeneratesCounter;
|
||||||
|
|
||||||
private $vendor;
|
private Client $client;
|
||||||
|
|
||||||
private $purchase_order;
|
private PurchaseOrder $purchase_order;
|
||||||
|
|
||||||
private $completed = true;
|
private bool $completed = true;
|
||||||
|
|
||||||
public function __construct(Vendor $vendor, PurchaseOrder $purchase_order)
|
public function __construct(Client $client, PurchaseOrder $purchase_order)
|
||||||
{
|
{
|
||||||
$this->vendor = $vendor;
|
$this->client = $client;
|
||||||
|
|
||||||
$this->purchase_order = $purchase_order;
|
$this->purchase_order = $purchase_order;
|
||||||
}
|
}
|
||||||
@ -40,47 +34,24 @@ class ApplyNumber extends AbstractService
|
|||||||
return $this->purchase_order;
|
return $this->purchase_order;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($this->client->getSetting('counter_number_applied')) {
|
$this->trySaving();
|
||||||
case 'when_saved':
|
|
||||||
$this->trySaving();
|
|
||||||
break;
|
|
||||||
case 'when_sent':
|
|
||||||
if ($this->purchase_order->status_id == PurchaseOrder::STATUS_SENT) {
|
|
||||||
$this->trySaving();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->purchase_order;
|
return $this->purchase_order;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function trySaving()
|
private function trySaving()
|
||||||
{
|
{
|
||||||
|
|
||||||
$x=1;
|
$x=1;
|
||||||
|
|
||||||
do{
|
do{
|
||||||
|
|
||||||
try{
|
try{
|
||||||
|
$this->purchase_order->number = $this->getNextPurchaseOrderNumber($this->client, $this->purchase_order);
|
||||||
$this->purchase_order->number = $this->getNextPurchaseOrderNumber($this->purchase_order);
|
|
||||||
$this->purchase_order->saveQuietly();
|
$this->purchase_order->saveQuietly();
|
||||||
|
|
||||||
$this->completed = false;
|
$this->completed = false;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
catch(QueryException $e){
|
catch(QueryException $e){
|
||||||
|
|
||||||
$x++;
|
$x++;
|
||||||
|
|
||||||
if($x>10)
|
if($x>10)
|
||||||
$this->completed = false;
|
$this->completed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
while($this->completed);
|
while($this->completed);
|
||||||
|
|
||||||
|
|||||||
@ -1,20 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
/**
|
|
||||||
* Invoice Ninja (https://invoiceninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2022. Invoice Ninja LLC (https://invoiceninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Services\PurchaseOrder;
|
namespace App\Services\PurchaseOrder;
|
||||||
|
|
||||||
use App\Factory\ClientContactFactory;
|
|
||||||
use App\Factory\PurchaseOrderInvitationFactory;
|
use App\Factory\PurchaseOrderInvitationFactory;
|
||||||
use App\Factory\VendorContactFactory;
|
|
||||||
use App\Models\Invoice;
|
|
||||||
use App\Models\PurchaseOrder;
|
use App\Models\PurchaseOrder;
|
||||||
use App\Models\PurchaseOrderInvitation;
|
use App\Models\PurchaseOrderInvitation;
|
||||||
use App\Services\AbstractService;
|
use App\Services\AbstractService;
|
||||||
@ -24,18 +14,24 @@ use Illuminate\Support\Str;
|
|||||||
class CreateInvitations extends AbstractService
|
class CreateInvitations extends AbstractService
|
||||||
{
|
{
|
||||||
use MakesHash;
|
use MakesHash;
|
||||||
|
|
||||||
private $purchase_order;
|
public PurchaseOrder $purchase_order;
|
||||||
|
|
||||||
public function __construct(PurchaseOrder $purchase_order)
|
public function __construct(PurchaseOrder $purchase_order)
|
||||||
{
|
{
|
||||||
$this->purchase_order = $purchase_order;
|
$this->purchase_order = $purchase_order;
|
||||||
}
|
}
|
||||||
|
private function createBlankContact()
|
||||||
|
{
|
||||||
|
$new_contact = PurchaseOrderInvitationFactory::create($this->purchase_order->company_id, $this->purchase_order->user_id);
|
||||||
|
$new_contact->client_id = $this->purchase_order->client_id;
|
||||||
|
$new_contact->contact_key = Str::random(40);
|
||||||
|
$new_contact->is_primary = true;
|
||||||
|
$new_contact->save();
|
||||||
|
}
|
||||||
public function run()
|
public function run()
|
||||||
{
|
{
|
||||||
|
$contacts = $this->purchase_order->vendor->contacts;
|
||||||
$contacts = $this->purchase_order->vendor->contacts()->where('send_email', true)->get();
|
|
||||||
|
|
||||||
if($contacts->count() == 0){
|
if($contacts->count() == 0){
|
||||||
$this->createBlankContact();
|
$this->createBlankContact();
|
||||||
@ -45,62 +41,51 @@ class CreateInvitations extends AbstractService
|
|||||||
}
|
}
|
||||||
|
|
||||||
$contacts->each(function ($contact) {
|
$contacts->each(function ($contact) {
|
||||||
$invitation = PurchaseOrderInvitation::where('company_id', $this->purchase_order->company_id)
|
$invitation = PurchaseOrderInvitation::whereCompanyId($this->purchase_order->company_id)
|
||||||
->where('vendor_contact_id', $contact->id)
|
->whereClientContactId($contact->id)
|
||||||
->where('purchase_order_id', $this->purchase_order->id)
|
->whereCreditId($this->purchase_order->id)
|
||||||
->withTrashed()
|
->withTrashed()
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
if (! $invitation && $contact->send_email) {
|
if (! $invitation) {
|
||||||
$ii = PurchaseOrderInvitationFactory::create($this->purchase_order->company_id, $this->purchase_order->user_id);
|
$ii = PurchaseOrderInvitation::create($this->purchase_order->company_id, $this->purchase_order->user_id);
|
||||||
$ii->key = $this->createDbHash($this->purchase_order->company->db);
|
$ii->key = $this->createDbHash($this->purchase_order->company->db);
|
||||||
$ii->purchase_order_id = $this->purchase_order->id;
|
$ii->purchase_order_id = $this->purchase_order->id;
|
||||||
$ii->vendor_contact_id = $contact->id;
|
$ii->vendor_contact_id = $contact->id;
|
||||||
$ii->save();
|
$ii->save();
|
||||||
} elseif ($invitation && ! $contact->send_email) {
|
} elseif (! $contact->send_email) {
|
||||||
$invitation->delete();
|
$invitation->delete();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if($this->purchase_order->invitations()->count() == 0) {
|
if($this->purchase_order->invitations()->count() == 0) {
|
||||||
|
|
||||||
if($contacts->count() == 0){
|
if($contacts->count() == 0){
|
||||||
$contact = $this->createBlankContact();
|
$contact = $this->createBlankContact();
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
$contact = $contacts->first();
|
$contact = $contacts->first();
|
||||||
|
|
||||||
$invitation = PurchaseOrderInvitation::where('company_id', $this->purchase_order->company_id)
|
$invitation = PurchaseOrder::where('company_id', $this->purchase_order->company_id)
|
||||||
->where('vendor_contact_id', $contact->id)
|
->where('vendor_contact_id', $contact->id)
|
||||||
->where('purchase_order_id', $this->purchase_order->id)
|
->where('purchase_order_id', $this->purchase_order->id)
|
||||||
->withTrashed()
|
->withTrashed()
|
||||||
->first();
|
->first();
|
||||||
|
|
||||||
if($invitation){
|
if($invitation){
|
||||||
$invitation->restore();
|
$invitation->restore();
|
||||||
return $this->purchase_order;
|
return $this->purchase_order;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$ii = PurchaseOrderInvitationFactory::create($this->purchase_order->company_id, $this->purchase_order->user_id);
|
$ii = PurchaseOrderInvitation::create($this->purchase_order->company_id, $this->purchase_order->user_id);
|
||||||
$ii->key = $this->createDbHash($this->purchase_order->company->db);
|
$ii->key = $this->createDbHash($this->purchase_order->company->db);
|
||||||
$ii->purchase_order_id = $this->purchase_order->id;
|
$ii->purchase_order_id = $this->purchase_order->id;
|
||||||
$ii->vendor_contact_id = $contact->id;
|
$ii->vendor_contact_id = $contact->id;
|
||||||
$ii->save();
|
$ii->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return $this->purchase_order;
|
return $this->purchase_order;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createBlankContact()
|
|
||||||
{
|
|
||||||
$new_contact = VendorContactFactory::create($this->purchase_order->company_id, $this->purchase_order->user_id);
|
|
||||||
$new_contact->vendor_id = $this->purchase_order->vendor_id;
|
|
||||||
$new_contact->contact_key = Str::random(40);
|
|
||||||
$new_contact->is_primary = true;
|
|
||||||
$new_contact->send_email = true;
|
|
||||||
$new_contact->save();
|
|
||||||
|
|
||||||
return $new_contact;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
45
app/Services/PurchaseOrder/MarkSent.php
Normal file
45
app/Services/PurchaseOrder/MarkSent.php
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace App\Services\PurchaseOrder;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Events\PurchaseOrder\PurchaseOrderWasMarkedSent;
|
||||||
|
use App\Models\PurchaseOrder;
|
||||||
|
use App\Utils\Ninja;
|
||||||
|
|
||||||
|
class MarkSent
|
||||||
|
{
|
||||||
|
private $vendor;
|
||||||
|
|
||||||
|
private $purchase_order;
|
||||||
|
|
||||||
|
public function __construct($vendor, $purchase_order)
|
||||||
|
{
|
||||||
|
$this->vendor = $vendor;
|
||||||
|
$this->purchase_order = $purchase_order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Return immediately if status is not draft */
|
||||||
|
if ($this->purchase_order->status_id != PurchaseOrder::STATUS_DRAFT) {
|
||||||
|
return $this->purchase_order;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->purchase_order->markInvitationsSent();
|
||||||
|
|
||||||
|
$this->purchase_order
|
||||||
|
->service()
|
||||||
|
->setStatus(PurchaseOrder::STATUS_SENT)
|
||||||
|
->applyNumber()
|
||||||
|
// ->adjustBalance($this->purchase_order->amount)
|
||||||
|
// ->touchPdf()
|
||||||
|
->save();
|
||||||
|
|
||||||
|
event(new PurchaseOrderWasMarkedSent($this->purchase_order, $this->purchase_order->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||||
|
|
||||||
|
return $this->purchase_order;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -71,4 +71,19 @@ class PurchaseOrderService
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setStatus($status)
|
||||||
|
{
|
||||||
|
$this->purchase_order->status_id = $status;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function markSent()
|
||||||
|
{
|
||||||
|
$this->purchase_order = (new MarkSent($this->purchase_order->vendor, $this->purchase_order))->run();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -106,6 +106,12 @@ class RecurringService
|
|||||||
$this->stop();
|
$this->stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(isset($this->recurring_entity->client))
|
||||||
|
{
|
||||||
|
$offset = $this->recurring_entity->client->timezone_offset();
|
||||||
|
$this->recurring_entity->next_send_date = Carbon::parse($this->recurring_entity->next_send_date_client)->startOfDay()->addSeconds($offset);
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -170,6 +170,10 @@ class CompanyTransformer extends EntityTransformer
|
|||||||
'markdown_email_enabled' => (bool) $company->markdown_email_enabled,
|
'markdown_email_enabled' => (bool) $company->markdown_email_enabled,
|
||||||
'stop_on_unpaid_recurring' => (bool) $company->stop_on_unpaid_recurring,
|
'stop_on_unpaid_recurring' => (bool) $company->stop_on_unpaid_recurring,
|
||||||
'use_quote_terms_on_conversion' => (bool) $company->use_quote_terms_on_conversion,
|
'use_quote_terms_on_conversion' => (bool) $company->use_quote_terms_on_conversion,
|
||||||
|
'stock_notification' => (bool) $company->stock_notification,
|
||||||
|
'inventory_notification_threshold' => (int) $company->inventory_notification_threshold,
|
||||||
|
'track_inventory' => (bool) $company->track_inventory,
|
||||||
|
'enable_applying_payments' => (bool) $company->enable_applying_payments,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -90,6 +90,9 @@ class ProductTransformer extends EntityTransformer
|
|||||||
'custom_value3' => $product->custom_value3 ?: '',
|
'custom_value3' => $product->custom_value3 ?: '',
|
||||||
'custom_value4' => $product->custom_value4 ?: '',
|
'custom_value4' => $product->custom_value4 ?: '',
|
||||||
'is_deleted' => (bool) $product->is_deleted,
|
'is_deleted' => (bool) $product->is_deleted,
|
||||||
|
'in_stock_quantity' => (int) $product->in_stock_quantity ?: 0,
|
||||||
|
'stock_notification' => (bool) $product->stock_notification,
|
||||||
|
'stock_notification_threshold' => (int) $product->stock_notification_threshold,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
app/Transformers/PurchaseOrderInvitationTransformer.php
Normal file
32
app/Transformers/PurchaseOrderInvitationTransformer.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace App\Transformers;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Models\PurchaseOrderInvitation;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
|
||||||
|
class PurchaseOrderInvitationTransformer extends EntityTransformer
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
|
||||||
|
public function transform(PurchaseOrderInvitation $invitation)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => $this->encodePrimaryKey($invitation->id),
|
||||||
|
'vendor_contact_id' => $this->encodePrimaryKey($invitation->vendor_contact_id),
|
||||||
|
'key' => $invitation->key,
|
||||||
|
'link' => $invitation->getLink() ?: '',
|
||||||
|
'sent_date' => $invitation->sent_date ?: '',
|
||||||
|
'viewed_date' => $invitation->viewed_date ?: '',
|
||||||
|
'opened_date' => $invitation->opened_date ?: '',
|
||||||
|
'updated_at' => (int)$invitation->updated_at,
|
||||||
|
'archived_at' => (int)$invitation->deleted_at,
|
||||||
|
'created_at' => (int)$invitation->created_at,
|
||||||
|
'email_status' => $invitation->email_status ?: '',
|
||||||
|
'email_error' => (string)$invitation->email_error,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -13,12 +13,24 @@ namespace App\Transformers;
|
|||||||
|
|
||||||
|
|
||||||
use App\Models\PurchaseOrder;
|
use App\Models\PurchaseOrder;
|
||||||
|
use App\Models\PurchaseOrderInvitation;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
|
||||||
class PurchaseOrderTransformer extends EntityTransformer
|
class PurchaseOrderTransformer extends EntityTransformer
|
||||||
{
|
{
|
||||||
use MakesHash;
|
use MakesHash;
|
||||||
|
|
||||||
|
protected $defaultIncludes = [
|
||||||
|
'invitations',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function includeInvitations(PurchaseOrder $purchase_order)
|
||||||
|
{
|
||||||
|
$transformer = new PurchaseOrderInvitationTransformer($this->serializer);
|
||||||
|
|
||||||
|
return $this->includeCollection($purchase_order->invitations, $transformer, PurchaseOrderInvitation::class);
|
||||||
|
}
|
||||||
|
|
||||||
public function transform(PurchaseOrder $purchase_order)
|
public function transform(PurchaseOrder $purchase_order)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@ -26,19 +38,18 @@ class PurchaseOrderTransformer extends EntityTransformer
|
|||||||
'user_id' => $this->encodePrimaryKey($purchase_order->user_id),
|
'user_id' => $this->encodePrimaryKey($purchase_order->user_id),
|
||||||
'project_id' => $this->encodePrimaryKey($purchase_order->project_id),
|
'project_id' => $this->encodePrimaryKey($purchase_order->project_id),
|
||||||
'assigned_user_id' => $this->encodePrimaryKey($purchase_order->assigned_user_id),
|
'assigned_user_id' => $this->encodePrimaryKey($purchase_order->assigned_user_id),
|
||||||
'vendor_id' => (string) $this->encodePrimaryKey($purchase_order->vendor_id),
|
'vendor_id' => (string)$this->encodePrimaryKey($purchase_order->vendor_id),
|
||||||
'amount' => (float) $purchase_order->amount,
|
'amount' => (float)$purchase_order->amount,
|
||||||
'balance' => (float) $purchase_order->balance,
|
'balance' => (float)$purchase_order->balance,
|
||||||
'client_id' => (string) $this->encodePrimaryKey($purchase_order->client_id),
|
'client_id' => (string)$this->encodePrimaryKey($purchase_order->client_id),
|
||||||
'vendor_id' => (string) $this->encodePrimaryKey($purchase_order->vendor_id),
|
'status_id' => (string)($purchase_order->status_id ?: 1),
|
||||||
'status_id' => (string) ($purchase_order->status_id ?: 1),
|
'design_id' => (string)$this->encodePrimaryKey($purchase_order->design_id),
|
||||||
'design_id' => (string) $this->encodePrimaryKey($purchase_order->design_id),
|
'created_at' => (int)$purchase_order->created_at,
|
||||||
'created_at' => (int) $purchase_order->created_at,
|
'updated_at' => (int)$purchase_order->updated_at,
|
||||||
'updated_at' => (int) $purchase_order->updated_at,
|
'archived_at' => (int)$purchase_order->deleted_at,
|
||||||
'archived_at' => (int) $purchase_order->deleted_at,
|
'is_deleted' => (bool)$purchase_order->is_deleted,
|
||||||
'is_deleted' => (bool) $purchase_order->is_deleted,
|
|
||||||
'number' => $purchase_order->number ?: '',
|
'number' => $purchase_order->number ?: '',
|
||||||
'discount' => (float) $purchase_order->discount,
|
'discount' => (float)$purchase_order->discount,
|
||||||
'po_number' => $purchase_order->po_number ?: '',
|
'po_number' => $purchase_order->po_number ?: '',
|
||||||
'date' => $purchase_order->date ?: '',
|
'date' => $purchase_order->date ?: '',
|
||||||
'last_sent_date' => $purchase_order->last_sent_date ?: '',
|
'last_sent_date' => $purchase_order->last_sent_date ?: '',
|
||||||
@ -51,36 +62,36 @@ class PurchaseOrderTransformer extends EntityTransformer
|
|||||||
'terms' => $purchase_order->terms ?: '',
|
'terms' => $purchase_order->terms ?: '',
|
||||||
'public_notes' => $purchase_order->public_notes ?: '',
|
'public_notes' => $purchase_order->public_notes ?: '',
|
||||||
'private_notes' => $purchase_order->private_notes ?: '',
|
'private_notes' => $purchase_order->private_notes ?: '',
|
||||||
'uses_inclusive_taxes' => (bool) $purchase_order->uses_inclusive_taxes,
|
'uses_inclusive_taxes' => (bool)$purchase_order->uses_inclusive_taxes,
|
||||||
'tax_name1' => $purchase_order->tax_name1 ? $purchase_order->tax_name1 : '',
|
'tax_name1' => $purchase_order->tax_name1 ? $purchase_order->tax_name1 : '',
|
||||||
'tax_rate1' => (float) $purchase_order->tax_rate1,
|
'tax_rate1' => (float)$purchase_order->tax_rate1,
|
||||||
'tax_name2' => $purchase_order->tax_name2 ? $purchase_order->tax_name2 : '',
|
'tax_name2' => $purchase_order->tax_name2 ? $purchase_order->tax_name2 : '',
|
||||||
'tax_rate2' => (float) $purchase_order->tax_rate2,
|
'tax_rate2' => (float)$purchase_order->tax_rate2,
|
||||||
'tax_name3' => $purchase_order->tax_name3 ? $purchase_order->tax_name3 : '',
|
'tax_name3' => $purchase_order->tax_name3 ? $purchase_order->tax_name3 : '',
|
||||||
'tax_rate3' => (float) $purchase_order->tax_rate3,
|
'tax_rate3' => (float)$purchase_order->tax_rate3,
|
||||||
'total_taxes' => (float) $purchase_order->total_taxes,
|
'total_taxes' => (float)$purchase_order->total_taxes,
|
||||||
'is_amount_discount' => (bool) ($purchase_order->is_amount_discount ?: false),
|
'is_amount_discount' => (bool)($purchase_order->is_amount_discount ?: false),
|
||||||
'footer' => $purchase_order->footer ?: '',
|
'footer' => $purchase_order->footer ?: '',
|
||||||
'partial' => (float) ($purchase_order->partial ?: 0.0),
|
'partial' => (float)($purchase_order->partial ?: 0.0),
|
||||||
'partial_due_date' => $purchase_order->partial_due_date ?: '',
|
'partial_due_date' => $purchase_order->partial_due_date ?: '',
|
||||||
'custom_value1' => (string) $purchase_order->custom_value1 ?: '',
|
'custom_value1' => (string)$purchase_order->custom_value1 ?: '',
|
||||||
'custom_value2' => (string) $purchase_order->custom_value2 ?: '',
|
'custom_value2' => (string)$purchase_order->custom_value2 ?: '',
|
||||||
'custom_value3' => (string) $purchase_order->custom_value3 ?: '',
|
'custom_value3' => (string)$purchase_order->custom_value3 ?: '',
|
||||||
'custom_value4' => (string) $purchase_order->custom_value4 ?: '',
|
'custom_value4' => (string)$purchase_order->custom_value4 ?: '',
|
||||||
'has_tasks' => (bool) $purchase_order->has_tasks,
|
'has_tasks' => (bool)$purchase_order->has_tasks,
|
||||||
'has_expenses' => (bool) $purchase_order->has_expenses,
|
'has_expenses' => (bool)$purchase_order->has_expenses,
|
||||||
'custom_surcharge1' => (float) $purchase_order->custom_surcharge1,
|
'custom_surcharge1' => (float)$purchase_order->custom_surcharge1,
|
||||||
'custom_surcharge2' => (float) $purchase_order->custom_surcharge2,
|
'custom_surcharge2' => (float)$purchase_order->custom_surcharge2,
|
||||||
'custom_surcharge3' => (float) $purchase_order->custom_surcharge3,
|
'custom_surcharge3' => (float)$purchase_order->custom_surcharge3,
|
||||||
'custom_surcharge4' => (float) $purchase_order->custom_surcharge4,
|
'custom_surcharge4' => (float)$purchase_order->custom_surcharge4,
|
||||||
'custom_surcharge_tax1' => (bool) $purchase_order->custom_surcharge_tax1,
|
'custom_surcharge_tax1' => (bool)$purchase_order->custom_surcharge_tax1,
|
||||||
'custom_surcharge_tax2' => (bool) $purchase_order->custom_surcharge_tax2,
|
'custom_surcharge_tax2' => (bool)$purchase_order->custom_surcharge_tax2,
|
||||||
'custom_surcharge_tax3' => (bool) $purchase_order->custom_surcharge_tax3,
|
'custom_surcharge_tax3' => (bool)$purchase_order->custom_surcharge_tax3,
|
||||||
'custom_surcharge_tax4' => (bool) $purchase_order->custom_surcharge_tax4,
|
'custom_surcharge_tax4' => (bool)$purchase_order->custom_surcharge_tax4,
|
||||||
'line_items' => $purchase_order->line_items ?: (array) [],
|
'line_items' => $purchase_order->line_items ?: (array)[],
|
||||||
'entity_type' => 'credit',
|
'entity_type' => 'credit',
|
||||||
'exchange_rate' => (float) $purchase_order->exchange_rate,
|
'exchange_rate' => (float)$purchase_order->exchange_rate,
|
||||||
'paid_to_date' => (float) $purchase_order->paid_to_date,
|
'paid_to_date' => (float)$purchase_order->paid_to_date,
|
||||||
'subscription_id' => $this->encodePrimaryKey($purchase_order->subscription_id),
|
'subscription_id' => $this->encodePrimaryKey($purchase_order->subscription_id),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -100,7 +100,8 @@ class RecurringExpenseTransformer extends EntityTransformer
|
|||||||
'frequency_id' => (string) $recurring_expense->frequency_id,
|
'frequency_id' => (string) $recurring_expense->frequency_id,
|
||||||
'remaining_cycles' => (int) $recurring_expense->remaining_cycles,
|
'remaining_cycles' => (int) $recurring_expense->remaining_cycles,
|
||||||
'last_sent_date' => $recurring_expense->last_sent_date ?: '',
|
'last_sent_date' => $recurring_expense->last_sent_date ?: '',
|
||||||
'next_send_date' => $recurring_expense->next_send_date ?: '',
|
// 'next_send_date' => $recurring_expense->next_send_date ?: '',
|
||||||
|
'next_send_date' => $recurring_expense->next_send_date_client ?: '',
|
||||||
'recurring_dates' => (array) [],
|
'recurring_dates' => (array) [],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@ -95,7 +95,8 @@ class RecurringInvoiceTransformer extends EntityTransformer
|
|||||||
'po_number' => $invoice->po_number ?: '',
|
'po_number' => $invoice->po_number ?: '',
|
||||||
'date' => $invoice->date ?: '',
|
'date' => $invoice->date ?: '',
|
||||||
'last_sent_date' => $invoice->last_sent_date ?: '',
|
'last_sent_date' => $invoice->last_sent_date ?: '',
|
||||||
'next_send_date' => $invoice->next_send_date ?: '',
|
// 'next_send_date' => $invoice->next_send_date ?: '',
|
||||||
|
'next_send_date' => $invoice->next_send_date_client ?: '',
|
||||||
'due_date' => $invoice->due_date ?: '',
|
'due_date' => $invoice->due_date ?: '',
|
||||||
'terms' => $invoice->terms ?: '',
|
'terms' => $invoice->terms ?: '',
|
||||||
'public_notes' => $invoice->public_notes ?: '',
|
'public_notes' => $invoice->public_notes ?: '',
|
||||||
|
|||||||
@ -45,8 +45,8 @@ trait GeneratesCounter
|
|||||||
|
|
||||||
$is_client_counter = false;
|
$is_client_counter = false;
|
||||||
|
|
||||||
$counter_string = $this->getEntityCounter($entity, $client);
|
$counter_string = $this->getEntityCounter($entity, $client);
|
||||||
$pattern = $this->getNumberPattern($entity, $client);
|
$pattern = $this->getNumberPattern($entity, $client);
|
||||||
|
|
||||||
if ((strpos($pattern, 'clientCounter') !== false) || (strpos($pattern, 'client_counter') !==false) ) {
|
if ((strpos($pattern, 'clientCounter') !== false) || (strpos($pattern, 'client_counter') !==false) ) {
|
||||||
|
|
||||||
@ -72,9 +72,9 @@ trait GeneratesCounter
|
|||||||
$counter_entity = $client->company;
|
$counter_entity = $client->company;
|
||||||
}
|
}
|
||||||
|
|
||||||
//If it is a quote - we need to
|
//If it is a quote - we need to
|
||||||
$pattern = $this->getNumberPattern($entity, $client);
|
$pattern = $this->getNumberPattern($entity, $client);
|
||||||
|
|
||||||
if(strlen($pattern) > 1 && (stripos($pattern, 'counter') === false)){
|
if(strlen($pattern) > 1 && (stripos($pattern, 'counter') === false)){
|
||||||
$pattern = $pattern.'{$counter}';
|
$pattern = $pattern.'{$counter}';
|
||||||
}
|
}
|
||||||
@ -128,9 +128,9 @@ trait GeneratesCounter
|
|||||||
break;
|
break;
|
||||||
case Quote::class:
|
case Quote::class:
|
||||||
|
|
||||||
if ($this->hasSharedCounter($client, 'quote'))
|
if ($this->hasSharedCounter($client, 'quote'))
|
||||||
return 'invoice_number_counter';
|
return 'invoice_number_counter';
|
||||||
|
|
||||||
return 'quote_number_counter';
|
return 'quote_number_counter';
|
||||||
break;
|
break;
|
||||||
case RecurringInvoice::class:
|
case RecurringInvoice::class:
|
||||||
@ -146,14 +146,17 @@ trait GeneratesCounter
|
|||||||
return 'payment_number_counter';
|
return 'payment_number_counter';
|
||||||
break;
|
break;
|
||||||
case Credit::class:
|
case Credit::class:
|
||||||
if ($this->hasSharedCounter($client, 'credit'))
|
if ($this->hasSharedCounter($client, 'credit'))
|
||||||
return 'invoice_number_counter';
|
return 'invoice_number_counter';
|
||||||
|
|
||||||
return 'credit_number_counter';
|
return 'credit_number_counter';
|
||||||
break;
|
break;
|
||||||
case Project::class:
|
case Project::class:
|
||||||
return 'project_number_counter';
|
return 'project_number_counter';
|
||||||
break;
|
break;
|
||||||
|
case PurchaseOrder::class:
|
||||||
|
return 'purchase_order_number_counter';
|
||||||
|
break;
|
||||||
|
|
||||||
case PurchaseOrder::class:
|
case PurchaseOrder::class:
|
||||||
return 'purchase_order_number_counter';
|
return 'purchase_order_number_counter';
|
||||||
@ -193,6 +196,20 @@ trait GeneratesCounter
|
|||||||
|
|
||||||
return $this->replaceUserVars($credit, $entity_number);
|
return $this->replaceUserVars($credit, $entity_number);
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Gets the next purchase order number.
|
||||||
|
*
|
||||||
|
* @param PurchaseOrder $purchase_order The purchase order
|
||||||
|
*
|
||||||
|
* @return string The next purchase order number.
|
||||||
|
*/
|
||||||
|
public function getNextPurchaseOrderNumber(Client $client, ?PurchaseOrder $purchase_order) :string
|
||||||
|
{
|
||||||
|
$entity_number = $this->getNextEntityNumber(PurchaseOrder::class, $client);
|
||||||
|
|
||||||
|
return $this->replaceUserVars($purchase_order, $entity_number);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -407,7 +424,7 @@ trait GeneratesCounter
|
|||||||
*
|
*
|
||||||
* @return bool True if has shared counter, False otherwise.
|
* @return bool True if has shared counter, False otherwise.
|
||||||
*/
|
*/
|
||||||
public function hasSharedCounter(Client $client, string $type = 'quote') : bool
|
public function hasSharedCounter(Client $client, string $type = 'quote') : bool
|
||||||
{
|
{
|
||||||
if($type == 'quote')
|
if($type == 'quote')
|
||||||
return (bool) $client->getSetting('shared_invoice_quote_counter');
|
return (bool) $client->getSetting('shared_invoice_quote_counter');
|
||||||
@ -460,9 +477,9 @@ trait GeneratesCounter
|
|||||||
public function checkNumberAvailable($class, $entity, $number) :bool
|
public function checkNumberAvailable($class, $entity, $number) :bool
|
||||||
{
|
{
|
||||||
|
|
||||||
if ($entity = $class::whereCompanyId($entity->company_id)->whereNumber($number)->withTrashed()->exists())
|
if ($entity = $class::whereCompanyId($entity->company_id)->whereNumber($number)->withTrashed()->exists())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -526,7 +543,7 @@ trait GeneratesCounter
|
|||||||
|
|
||||||
if($reset_counter_frequency == 0)
|
if($reset_counter_frequency == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
$timezone = Timezone::find($client->getSetting('timezone_id'));
|
$timezone = Timezone::find($client->getSetting('timezone_id'));
|
||||||
|
|
||||||
$reset_date = Carbon::parse($client->getSetting('reset_counter_date'), $timezone->name);
|
$reset_date = Carbon::parse($client->getSetting('reset_counter_date'), $timezone->name);
|
||||||
@ -580,6 +597,7 @@ trait GeneratesCounter
|
|||||||
$settings->invoice_number_counter = 1;
|
$settings->invoice_number_counter = 1;
|
||||||
$settings->quote_number_counter = 1;
|
$settings->quote_number_counter = 1;
|
||||||
$settings->credit_number_counter = 1;
|
$settings->credit_number_counter = 1;
|
||||||
|
$settings->purchase_order_number_counter = 1;
|
||||||
|
|
||||||
$client->company->settings = $settings;
|
$client->company->settings = $settings;
|
||||||
$client->company->save();
|
$client->company->save();
|
||||||
@ -644,6 +662,7 @@ trait GeneratesCounter
|
|||||||
$settings->task_number_counter = 1;
|
$settings->task_number_counter = 1;
|
||||||
$settings->expense_number_counter = 1;
|
$settings->expense_number_counter = 1;
|
||||||
$settings->recurring_expense_number_counter =1;
|
$settings->recurring_expense_number_counter =1;
|
||||||
|
$settings->purchase_order_number_counter = 1;
|
||||||
|
|
||||||
$company->settings = $settings;
|
$company->settings = $settings;
|
||||||
$company->save();
|
$company->save();
|
||||||
@ -666,7 +685,7 @@ trait GeneratesCounter
|
|||||||
|
|
||||||
$search = [];
|
$search = [];
|
||||||
$replace = [];
|
$replace = [];
|
||||||
|
|
||||||
$search[] = '{$counter}';
|
$search[] = '{$counter}';
|
||||||
$replace[] = $counter;
|
$replace[] = $counter;
|
||||||
|
|
||||||
@ -681,7 +700,7 @@ trait GeneratesCounter
|
|||||||
|
|
||||||
$search[] = '{$year}';
|
$search[] = '{$year}';
|
||||||
$replace[] = Carbon::now($entity->company->timezone()->name)->format('Y');
|
$replace[] = Carbon::now($entity->company->timezone()->name)->format('Y');
|
||||||
|
|
||||||
if (strstr($pattern, '{$user_id}') || strstr($pattern, '{$userId}')) {
|
if (strstr($pattern, '{$user_id}') || strstr($pattern, '{$userId}')) {
|
||||||
$user_id = $entity->user_id ? $entity->user_id : 0;
|
$user_id = $entity->user_id ? $entity->user_id : 0;
|
||||||
$search[] = '{$user_id}';
|
$search[] = '{$user_id}';
|
||||||
@ -705,7 +724,7 @@ trait GeneratesCounter
|
|||||||
$search[] = '{$vendor_id_number}';
|
$search[] = '{$vendor_id_number}';
|
||||||
$replace[] = $entity->id_number;
|
$replace[] = $entity->id_number;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($entity instanceof Expense) {
|
if ($entity instanceof Expense) {
|
||||||
if ($entity->vendor) {
|
if ($entity->vendor) {
|
||||||
$search[] = '{$vendor_id_number}';
|
$search[] = '{$vendor_id_number}';
|
||||||
@ -730,7 +749,7 @@ trait GeneratesCounter
|
|||||||
$search[] = '{$expense_id_number}';
|
$search[] = '{$expense_id_number}';
|
||||||
$replace[] = $entity->id_number;
|
$replace[] = $entity->id_number;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($entity->client || ($entity instanceof Client)) {
|
if ($entity->client || ($entity instanceof Client)) {
|
||||||
$client = $entity->client ?: $entity;
|
$client = $entity->client ?: $entity;
|
||||||
|
|
||||||
|
|||||||
@ -14,8 +14,8 @@ return [
|
|||||||
'require_https' => env('REQUIRE_HTTPS', true),
|
'require_https' => env('REQUIRE_HTTPS', true),
|
||||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||||
'app_version' => '5.3.93',
|
'app_version' => '5.3.96',
|
||||||
'app_tag' => '5.3.93',
|
'app_tag' => '5.3.96',
|
||||||
'minimum_client_version' => '5.0.16',
|
'minimum_client_version' => '5.0.16',
|
||||||
'terms_version' => '1.0.1',
|
'terms_version' => '1.0.1',
|
||||||
'api_secret' => env('API_SECRET', ''),
|
'api_secret' => env('API_SECRET', ''),
|
||||||
|
|||||||
31
database/factories/PurchaseOrderInvitationFactory.php
Normal file
31
database/factories/PurchaseOrderInvitationFactory.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Models\PurchaseOrderInvitation;
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class PurchaseOrderInvitationFactory extends Factory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The name of the factory's corresponding model.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $model = PurchaseOrderInvitation::class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define the model's default state.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function definition()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'key' => Str::random(40),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -51,6 +51,7 @@ class RecurringInvoiceFactory extends Factory
|
|||||||
'frequency_id' => RecurringInvoice::FREQUENCY_MONTHLY,
|
'frequency_id' => RecurringInvoice::FREQUENCY_MONTHLY,
|
||||||
'last_sent_date' => now()->subMonth(),
|
'last_sent_date' => now()->subMonth(),
|
||||||
'next_send_date' => now()->addMonthNoOverflow(),
|
'next_send_date' => now()->addMonthNoOverflow(),
|
||||||
|
'next_send_date_client' => now()->addMonthNoOverflow(),
|
||||||
'remaining_cycles' => $this->faker->numberBetween(1, 10),
|
'remaining_cycles' => $this->faker->numberBetween(1, 10),
|
||||||
'amount' => $this->faker->randomFloat(2, $min = 1, $max = 1000), // 48.8932
|
'amount' => $this->faker->randomFloat(2, $min = 1, $max = 1000), // 48.8932
|
||||||
|
|
||||||
|
|||||||
@ -30,9 +30,6 @@ class CreateSchedulersTable extends Migration
|
|||||||
$table->timestamp('start_from');
|
$table->timestamp('start_from');
|
||||||
$table->timestamp('scheduled_run');
|
$table->timestamp('scheduled_run');
|
||||||
$table->foreignIdFor(\App\Models\Company::class);
|
$table->foreignIdFor(\App\Models\Company::class);
|
||||||
$table->string('action_name')->index();
|
|
||||||
$table->string('action_class');
|
|
||||||
$table->json('parameters')->nullable();
|
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
$table->softDeletes();
|
$table->softDeletes();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -122,6 +122,5 @@ class CreatePurchaseOrdersTable extends Migration
|
|||||||
*/
|
*/
|
||||||
public function down()
|
public function down()
|
||||||
{
|
{
|
||||||
Schema::dropIfExists('purchase_orders');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,40 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2021. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://opensource.org/licenses/AAL
|
||||||
|
*/
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class AddJobRelatedFieldsToSchedulersTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('schedulers', function (Blueprint $table) {
|
||||||
|
$table->string('action_name')->index();
|
||||||
|
$table->string('action_class');
|
||||||
|
$table->json('parameters')->nullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class InventoryManagementSchema extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
Schema::table('companies', function (Blueprint $table) {
|
||||||
|
$table->boolean('enable_applying_payments')->default(0);
|
||||||
|
$table->boolean('track_inventory')->default(0);
|
||||||
|
$table->integer('inventory_notification_threshold')->default(0);
|
||||||
|
$table->boolean('stock_notification')->default(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('products', function (Blueprint $table){
|
||||||
|
$table->integer('in_stock_quantity')->default(0);
|
||||||
|
$table->boolean('stock_notification')->default(1);
|
||||||
|
$table->integer('stock_notification_threshold')->default(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\RecurringExpense;
|
||||||
|
use App\Models\RecurringInvoice;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class SetRecurringClientTimestamp extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::table('recurring_invoices', function (Blueprint $table) {
|
||||||
|
$table->datetime('next_send_date_client')->nullable();
|
||||||
|
});
|
||||||
|
|
||||||
|
Schema::table('recurring_expenses', function (Blueprint $table) {
|
||||||
|
$table->datetime('next_send_date_client')->nullable();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
RecurringInvoice::whereNotNull('next_send_date')->cursor()->each(function ($recurring_invoice){
|
||||||
|
|
||||||
|
// $offset = $recurring_invoice->client->timezone_offset();
|
||||||
|
// $re = Carbon::parse($recurring_invoice->next_send_date)->subSeconds($offset)->format('Y-m-d');
|
||||||
|
$re = Carbon::parse($recurring_invoice->next_send_date)->format('Y-m-d');
|
||||||
|
$recurring_invoice->next_send_date_client = $re;
|
||||||
|
$recurring_invoice->saveQuietly();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
RecurringExpense::whereNotNull('next_send_date')->cursor()->each(function ($recurring_expense){
|
||||||
|
$recurring_expense->next_send_date_client = $recurring_expense->next_send_date;
|
||||||
|
$recurring_expense->saveQuietly();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class CreatePurchaseOrderInvitationsTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
Schema::create('purchase_order_invitations', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->unsignedInteger('company_id')->index();
|
||||||
|
$table->unsignedInteger('user_id');
|
||||||
|
$table->unsignedInteger('vendor_contact_id')->unique();
|
||||||
|
$table->unsignedBigInteger('purchase_order_id')->index()->unique();
|
||||||
|
$table->string('key')->index();
|
||||||
|
$table->string('transaction_reference')->nullable();
|
||||||
|
$table->string('message_id')->nullable()->index();
|
||||||
|
$table->mediumText('email_error')->nullable();
|
||||||
|
$table->text('signature_base64')->nullable();
|
||||||
|
$table->datetime('signature_date')->nullable();
|
||||||
|
|
||||||
|
$table->datetime('sent_date')->nullable();
|
||||||
|
$table->datetime('viewed_date')->nullable();
|
||||||
|
$table->datetime('opened_date')->nullable();
|
||||||
|
|
||||||
|
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
|
||||||
|
$table->foreign('vendor_contact_id')->references('id')->on('vendor_contacts')->onDelete('cascade')->onUpdate('cascade');
|
||||||
|
$table->foreign('purchase_order_id')->references('id')->on('purchase_orders')->onDelete('cascade')->onUpdate('cascade');
|
||||||
|
$table->foreign('company_id')->references('id')->on('companies')->onDelete('cascade')->onUpdate('cascade');
|
||||||
|
|
||||||
|
$table->timestamps(6);
|
||||||
|
$table->softDeletes('deleted_at', 6);
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('purchase_order_invitations');
|
||||||
|
}
|
||||||
|
}
|
||||||
48
public/flutter_service_worker.js
vendored
48
public/flutter_service_worker.js
vendored
@ -3,43 +3,43 @@ const MANIFEST = 'flutter-app-manifest';
|
|||||||
const TEMP = 'flutter-temp-cache';
|
const TEMP = 'flutter-temp-cache';
|
||||||
const CACHE_NAME = 'flutter-app-cache';
|
const CACHE_NAME = 'flutter-app-cache';
|
||||||
const RESOURCES = {
|
const RESOURCES = {
|
||||||
"main.dart.js": "208309d12730fc06c9bf9ef0bd0c2483",
|
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
|
||||||
|
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
|
||||||
|
"/": "90082634bee3634faebfddf446a803a2",
|
||||||
|
"main.dart.js": "cf8b4f4a686adceb3f1085f27a463220",
|
||||||
|
"version.json": "3afb81924daf4f751571755436069115",
|
||||||
|
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
|
||||||
|
"flutter.js": "0816e65a103ba8ba51b174eeeeb2cb67",
|
||||||
|
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
||||||
"canvaskit/canvaskit.wasm": "4b83d89d9fecbea8ca46f2f760c5a9ba",
|
"canvaskit/canvaskit.wasm": "4b83d89d9fecbea8ca46f2f760c5a9ba",
|
||||||
"canvaskit/profiling/canvaskit.wasm": "95e736ab31147d1b2c7b25f11d4c32cd",
|
"canvaskit/profiling/canvaskit.wasm": "95e736ab31147d1b2c7b25f11d4c32cd",
|
||||||
"canvaskit/profiling/canvaskit.js": "ae2949af4efc61d28a4a80fffa1db900",
|
"canvaskit/profiling/canvaskit.js": "ae2949af4efc61d28a4a80fffa1db900",
|
||||||
"canvaskit/canvaskit.js": "c2b4e5f3d7a3d82aed024e7249a78487",
|
"canvaskit/canvaskit.js": "c2b4e5f3d7a3d82aed024e7249a78487",
|
||||||
"flutter.js": "0816e65a103ba8ba51b174eeeeb2cb67",
|
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
|
||||||
"/": "d60cdb0e60692160d5f962b98b88e24d",
|
|
||||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
|
|
||||||
"version.json": "3afb81924daf4f751571755436069115",
|
|
||||||
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5",
|
"assets/AssetManifest.json": "38d9aea341601f3a5c6fa7b5a1216ea5",
|
||||||
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "b62641afc9ab487008e996a5c5865e56",
|
"assets/packages/material_design_icons_flutter/lib/fonts/materialdesignicons-webfont.ttf": "b62641afc9ab487008e996a5c5865e56",
|
||||||
"assets/assets/images/icon.png": "090f69e23311a4b6d851b3880ae52541",
|
"assets/fonts/MaterialIcons-Regular.otf": "95db9098c58fd6db106f1116bae85a0b",
|
||||||
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
|
"assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219",
|
||||||
"assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c",
|
|
||||||
"assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5",
|
"assets/assets/images/payment_types/discover.png": "6c0a386a00307f87db7bea366cca35f5",
|
||||||
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
"assets/assets/images/payment_types/carteblanche.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
||||||
"assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc",
|
|
||||||
"assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024",
|
|
||||||
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
|
|
||||||
"assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71",
|
|
||||||
"assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
|
|
||||||
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
|
|
||||||
"assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978",
|
|
||||||
"assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868",
|
|
||||||
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
|
|
||||||
"assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
"assets/assets/images/payment_types/other.png": "d936e11fa3884b8c9f1bd5c914be8629",
|
||||||
|
"assets/assets/images/payment_types/laser.png": "b4e6e93dd35517ac429301119ff05868",
|
||||||
|
"assets/assets/images/payment_types/ach.png": "7433f0aff779dc98a649b7a2daf777cf",
|
||||||
|
"assets/assets/images/payment_types/dinerscard.png": "06d85186ba858c18ab7c9caa42c92024",
|
||||||
|
"assets/assets/images/payment_types/unionpay.png": "7002f52004e0ab8cc0b7450b0208ccb2",
|
||||||
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
|
"assets/assets/images/payment_types/maestro.png": "e533b92bfb50339fdbfa79e3dfe81f08",
|
||||||
|
"assets/assets/images/payment_types/paypal.png": "8e06c094c1871376dfea1da8088c29d1",
|
||||||
|
"assets/assets/images/payment_types/visa.png": "3ddc4a4d25c946e8ad7e6998f30fd4e3",
|
||||||
|
"assets/assets/images/payment_types/solo.png": "2030c3ccaccf5d5e87916a62f5b084d6",
|
||||||
|
"assets/assets/images/payment_types/amex.png": "c49a4247984b3732a4af50a3390aa978",
|
||||||
|
"assets/assets/images/payment_types/switch.png": "4fa11c45327f5fdc20205821b2cfd9cc",
|
||||||
|
"assets/assets/images/payment_types/jcb.png": "07e0942d16c5592118b72e74f2f7198c",
|
||||||
|
"assets/assets/images/payment_types/mastercard.png": "6f6cdc29ee2e22e06b1ac029cb52ef71",
|
||||||
|
"assets/assets/images/icon.png": "090f69e23311a4b6d851b3880ae52541",
|
||||||
"assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3",
|
"assets/assets/images/google_logo.png": "0f118259ce403274f407f5e982e681c3",
|
||||||
"assets/assets/images/logo_light.png": "e5f46d5a78e226e7a9553d4ca6f69219",
|
|
||||||
"assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a",
|
"assets/assets/images/logo_dark.png": "a233ed1d4d0f7414bf97a9a10f11fb0a",
|
||||||
"assets/NOTICES": "52d7174bb068ef86545951d5bc8c5744",
|
"assets/NOTICES": "52d7174bb068ef86545951d5bc8c5744",
|
||||||
"assets/FontManifest.json": "cf3c681641169319e61b61bd0277378f",
|
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40"
|
||||||
"assets/fonts/MaterialIcons-Regular.otf": "95db9098c58fd6db106f1116bae85a0b",
|
|
||||||
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
|
|
||||||
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35",
|
|
||||||
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
|
|
||||||
"favicon.ico": "51636d3a390451561744c42188ccd628"
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// The application shell files that are downloaded before a service worker can
|
// The application shell files that are downloaded before a service worker can
|
||||||
|
|||||||
263138
public/main.dart.js
vendored
263138
public/main.dart.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
260422
public/main.foss.dart.js
vendored
260422
public/main.foss.dart.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
255260
public/main.html.dart.js
vendored
255260
public/main.html.dart.js
vendored
File diff suppressed because one or more lines are too long
260344
public/main.next.dart.js
vendored
260344
public/main.next.dart.js
vendored
File diff suppressed because one or more lines are too long
15839
public/main.profile.dart.js
vendored
15839
public/main.profile.dart.js
vendored
File diff suppressed because one or more lines are too long
@ -117,13 +117,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td align="center">
|
||||||
<div class="dark-bg"
|
<div class="dark-bg"
|
||||||
style="background-color:#f9f9f9; border: 1px solid #c2c2c2; border-bottom: none; padding-bottom: 20px; border-top-left-radius: 3px; border-top-right-radius: 3px;">
|
style="background-color:#f9f9f9; border: 1px solid #c2c2c2; border-bottom: none; padding-bottom: 20px; border-top-left-radius: 3px; border-top-right-radius: 3px;">
|
||||||
<img class="logo-light"
|
|
||||||
style="margin-top: 20px; max-width: 155px; display: block; margin-left: auto; margin-right: auto; "
|
<!--[if gte mso 9]>
|
||||||
src="{{ $logo ?? '' }}"
|
<img src="{{ $logo ?? '' }}" alt="" width="155" border="0" align="middle" style="display:block;" />
|
||||||
width="155" />
|
<div style="mso-hide:all;">
|
||||||
|
<![endif]-->
|
||||||
|
<img class="logo-light" src="{{ $logo ?? '' }}" alt="" style="margin-top: 10px; max-width: 570px; display: block; margin-left: auto; margin-right: auto;"/>
|
||||||
|
<!--[if gte mso 9]>
|
||||||
|
</div>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -94,19 +94,24 @@
|
|||||||
<div style="text-align: center;margin-top: 25px; margin-bottom: 10px;"></div>
|
<div style="text-align: center;margin-top: 25px; margin-bottom: 10px;"></div>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td align="center" cellpadding="20">
|
||||||
<div
|
<div
|
||||||
style="border: 1px solid #c2c2c2; border-bottom: none; padding-bottom: 10px; border-top-left-radius: 3px; border-top-right-radius: 3px;">
|
style="border: 1px solid #c2c2c2; border-bottom: none; padding-bottom: 10px; border-top-left-radius: 3px; border-top-right-radius: 3px;">
|
||||||
|
|
||||||
<img
|
<!--[if gte mso 9]>
|
||||||
style="margin-top: 40px; height: 40px; display: block; margin-left: auto; margin-right: auto;"
|
<img src="{{ $logo ?? '' }}" alt="" width="400" border="0" align="middle" style="display:block;" />
|
||||||
alt=""
|
<div style="mso-hide:all;">
|
||||||
src="{{ $logo ?? '' }}"/>
|
<![endif]-->
|
||||||
|
<img src="{{ $logo ?? '' }}" alt="" style="margin-top: 40px; max-width: 155px; display: block; margin-left: auto; margin-right: auto;"/>
|
||||||
|
<!--[if gte mso 9]>
|
||||||
|
</div>
|
||||||
|
<![endif]-->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td cellpadding="20">
|
||||||
<div style="border: 1px solid #c2c2c2; border-top: none; border-bottom: none; padding: 20px; text-align: center" id="content">
|
<div style="border: 1px solid #c2c2c2; border-top: none; border-bottom: none; padding: 20px; text-align: center" id="content">
|
||||||
<div style="padding-top: 10px;"></div>
|
<div style="padding-top: 10px;"></div>
|
||||||
|
|
||||||
@ -120,10 +125,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td height="20">
|
||||||
|
<div style="border: 1px solid #c2c2c2; border-top: none; border-bottom: none; padding: 20px; text-align: center" id="content"> </div>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td cellpadding="20" bgcolor="#f9f9f9">
|
||||||
<div class="dark-bg dark-text-white"
|
<div class="dark-bg dark-text-white"
|
||||||
style="text-align: center; padding-top: 10px; padding-bottom: 25px; background-color: #f9f9f9; border: 1px solid #c2c2c2; border-top: none; border-bottom-color: #f9f9f9;">
|
style="text-align: center; padding-top: 10px; padding-bottom: 25px; background-color: #f9f9f9; border: 1px solid #c2c2c2; border-top: none; border-bottom-color: #f9f9f9;">
|
||||||
@isset($signature)
|
@isset($signature)
|
||||||
@ -145,7 +156,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td bgcolor="#242424" cellpadding="20">
|
||||||
<div class="dark-bg-base"
|
<div class="dark-bg-base"
|
||||||
style="padding-top: 10px;padding-bottom: 10px; background-color: #242424; border: 1px solid #c2c2c2; border-top-color: #242424; border-bottom-color: #242424;">
|
style="padding-top: 10px;padding-bottom: 10px; background-color: #242424; border: 1px solid #c2c2c2; border-top-color: #242424; border-bottom-color: #242424;">
|
||||||
@if(isset($company))
|
@if(isset($company))
|
||||||
|
|||||||
@ -37,6 +37,7 @@ class PurchaseOrderTest extends TestCase
|
|||||||
Model::reguard();
|
Model::reguard();
|
||||||
|
|
||||||
$this->makeTestData();
|
$this->makeTestData();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPurchaseOrderRest()
|
public function testPurchaseOrderRest()
|
||||||
@ -44,18 +45,18 @@ class PurchaseOrderTest extends TestCase
|
|||||||
$response = $this->withHeaders([
|
$response = $this->withHeaders([
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
'X-API-TOKEN' => $this->token,
|
'X-API-TOKEN' => $this->token,
|
||||||
])->get('/api/v1/purchase_orders/'.$this->encodePrimaryKey($this->purchase_order->id));
|
])->get('/api/v1/purchase_orders/' . $this->encodePrimaryKey($this->purchase_order->id));
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
|
|
||||||
$response = $this->withHeaders([
|
$response = $this->withHeaders([
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
'X-API-TOKEN' => $this->token,
|
'X-API-TOKEN' => $this->token,
|
||||||
])->get('/api/v1/purchase_orders/'.$this->encodePrimaryKey($this->purchase_order->id).'/edit');
|
])->get('/api/v1/purchase_orders/' . $this->encodePrimaryKey($this->purchase_order->id) . '/edit');
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
|
|
||||||
$credit_update = [
|
$purchase_order_update = [
|
||||||
'tax_name1' => 'dippy',
|
'tax_name1' => 'dippy',
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -64,14 +65,14 @@ class PurchaseOrderTest extends TestCase
|
|||||||
$response = $this->withHeaders([
|
$response = $this->withHeaders([
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
'X-API-TOKEN' => $this->token,
|
'X-API-TOKEN' => $this->token,
|
||||||
])->put('/api/v1/purchase_orders/'.$this->encodePrimaryKey($this->purchase_order->id), $credit_update)
|
])->put('/api/v1/purchase_orders/' . $this->encodePrimaryKey($this->purchase_order->id), $purchase_order_update)
|
||||||
->assertStatus(200);
|
->assertStatus(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPostNewPurchaseOrder()
|
public function testPostNewPurchaseOrder()
|
||||||
{
|
{
|
||||||
$purchase_order = [
|
$purchase_order = [
|
||||||
'status_id' => 1,
|
'status_id' => 1,
|
||||||
'number' => 'dfdfd',
|
|
||||||
'discount' => 0,
|
'discount' => 0,
|
||||||
'is_amount_discount' => 1,
|
'is_amount_discount' => 1,
|
||||||
'number' => '34343xx43',
|
'number' => '34343xx43',
|
||||||
@ -91,20 +92,21 @@ class PurchaseOrderTest extends TestCase
|
|||||||
])->post('/api/v1/purchase_orders/', $purchase_order)
|
])->post('/api/v1/purchase_orders/', $purchase_order)
|
||||||
->assertStatus(200);
|
->assertStatus(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPurchaseOrderDelete()
|
public function testPurchaseOrderDelete()
|
||||||
{
|
{
|
||||||
$response = $this->withHeaders([
|
$response = $this->withHeaders([
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
'X-API-TOKEN' => $this->token,
|
'X-API-TOKEN' => $this->token,
|
||||||
])->delete('/api/v1/purchase_orders/'.$this->encodePrimaryKey($this->purchase_order->id));
|
])->delete('/api/v1/purchase_orders/' . $this->encodePrimaryKey($this->purchase_order->id));
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPurchaseOrderUpdate()
|
public function testPurchaseOrderUpdate()
|
||||||
{
|
{
|
||||||
$data = [
|
$data = [
|
||||||
'status_id' => 1,
|
'status_id' => 1,
|
||||||
'number' => 'dfdfd',
|
|
||||||
'discount' => 0,
|
'discount' => 0,
|
||||||
'is_amount_discount' => 1,
|
'is_amount_discount' => 1,
|
||||||
'number' => '3434343',
|
'number' => '3434343',
|
||||||
@ -121,14 +123,14 @@ class PurchaseOrderTest extends TestCase
|
|||||||
$response = $this->withHeaders([
|
$response = $this->withHeaders([
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
'X-API-TOKEN' => $this->token,
|
'X-API-TOKEN' => $this->token,
|
||||||
])->put('/api/v1/purchase_orders/'.$this->encodePrimaryKey($this->purchase_order->id), $data);
|
])->put('/api/v1/purchase_orders/' . $this->encodePrimaryKey($this->purchase_order->id), $data);
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
|
|
||||||
$response = $this->withHeaders([
|
$response = $this->withHeaders([
|
||||||
'X-API-SECRET' => config('ninja.api_secret'),
|
'X-API-SECRET' => config('ninja.api_secret'),
|
||||||
'X-API-TOKEN' => $this->token,
|
'X-API-TOKEN' => $this->token,
|
||||||
])->put('/api/v1/purchase_orders/'.$this->encodePrimaryKey($this->purchase_order->id), $data);
|
])->put('/api/v1/purchase_orders/' . $this->encodePrimaryKey($this->purchase_order->id), $data);
|
||||||
|
|
||||||
$response->assertStatus(200);
|
$response->assertStatus(200);
|
||||||
|
|
||||||
|
|||||||
@ -36,6 +36,8 @@ use App\Models\GroupSetting;
|
|||||||
use App\Models\InvoiceInvitation;
|
use App\Models\InvoiceInvitation;
|
||||||
use App\Models\Product;
|
use App\Models\Product;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
|
use App\Models\PurchaseOrder;
|
||||||
|
use App\Models\PurchaseOrderInvitation;
|
||||||
use App\Models\Quote;
|
use App\Models\Quote;
|
||||||
use App\Models\QuoteInvitation;
|
use App\Models\QuoteInvitation;
|
||||||
use App\Models\RecurringExpense;
|
use App\Models\RecurringExpense;
|
||||||
@ -476,6 +478,26 @@ trait MockAccountData
|
|||||||
$this->purchase_order->save();
|
$this->purchase_order->save();
|
||||||
|
|
||||||
|
|
||||||
|
PurchaseOrderInvitation::factory()->create([
|
||||||
|
'user_id' => $user_id,
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'vendor_contact_id' => $vendor_contact->id,
|
||||||
|
'purchase_order_id' => $this->purchase_order->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
$purchase_order_invitations = PurchaseOrderInvitation::whereCompanyId($this->purchase_order->company_id)
|
||||||
|
->wherePurchaseOrderId($this->purchase_order->id);
|
||||||
|
|
||||||
|
$this->purchase_order->setRelation('invitations', $purchase_order_invitations);
|
||||||
|
|
||||||
|
$this->purchase_order->service()->markSent();
|
||||||
|
|
||||||
|
$this->purchase_order->setRelation('client', $this->client);
|
||||||
|
$this->purchase_order->setRelation('company', $this->company);
|
||||||
|
|
||||||
|
$this->purchase_order->save();
|
||||||
|
|
||||||
|
|
||||||
$this->credit = CreditFactory::create($this->company->id, $user_id);
|
$this->credit = CreditFactory::create($this->company->id, $user_id);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user