mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-06-01 15:34:33 -04:00
commit
f2bfca648f
@ -276,6 +276,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 = [
|
||||||
'page_numbering_alignment' => 'string',
|
'page_numbering_alignment' => 'string',
|
||||||
'page_numbering' => 'bool',
|
'page_numbering' => 'bool',
|
||||||
@ -474,6 +477,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;
|
||||||
|
}
|
||||||
|
}
|
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;
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
@ -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);
|
||||||
|
@ -12,8 +12,12 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Jobs\Entity\CreateEntityPdf;
|
||||||
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
|
||||||
{
|
{
|
||||||
@ -129,11 +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()
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 => [
|
||||||
|
],
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ namespace App\Repositories;
|
|||||||
|
|
||||||
|
|
||||||
use App\Models\PurchaseOrder;
|
use App\Models\PurchaseOrder;
|
||||||
|
use App\Models\PurchaseOrderInvitation;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
|
|
||||||
class PurchaseOrderRepository extends BaseRepository
|
class PurchaseOrderRepository extends BaseRepository
|
||||||
@ -30,5 +31,9 @@ class PurchaseOrderRepository extends BaseRepository
|
|||||||
|
|
||||||
return $purchase_order;
|
return $purchase_order;
|
||||||
}
|
}
|
||||||
|
public function getInvitationByKey($key) :?PurchaseOrderInvitation
|
||||||
|
{
|
||||||
|
return PurchaseOrderInvitation::where('key', $key)->first();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
59
app/Services/PurchaseOrder/ApplyNumber.php
Normal file
59
app/Services/PurchaseOrder/ApplyNumber.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace App\Services\PurchaseOrder;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Models\Client;
|
||||||
|
use App\Models\Credit;
|
||||||
|
use App\Models\PurchaseOrder;
|
||||||
|
use App\Services\AbstractService;
|
||||||
|
use App\Utils\Traits\GeneratesCounter;
|
||||||
|
use Illuminate\Database\QueryException;
|
||||||
|
|
||||||
|
class ApplyNumber extends AbstractService
|
||||||
|
{
|
||||||
|
use GeneratesCounter;
|
||||||
|
|
||||||
|
private Client $client;
|
||||||
|
|
||||||
|
private PurchaseOrder $purchase_order;
|
||||||
|
|
||||||
|
private bool $completed = true;
|
||||||
|
|
||||||
|
public function __construct(Client $client, PurchaseOrder $purchase_order)
|
||||||
|
{
|
||||||
|
$this->client = $client;
|
||||||
|
|
||||||
|
$this->purchase_order = $purchase_order;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run()
|
||||||
|
{
|
||||||
|
if ($this->purchase_order->number != '') {
|
||||||
|
return $this->purchase_order;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->trySaving();
|
||||||
|
|
||||||
|
return $this->purchase_order;
|
||||||
|
}
|
||||||
|
private function trySaving()
|
||||||
|
{
|
||||||
|
$x=1;
|
||||||
|
do{
|
||||||
|
try{
|
||||||
|
$this->purchase_order->number = $this->getNextPurchaseOrderNumber($this->client, $this->purchase_order);
|
||||||
|
$this->purchase_order->saveQuietly();
|
||||||
|
$this->completed = false;
|
||||||
|
}
|
||||||
|
catch(QueryException $e){
|
||||||
|
$x++;
|
||||||
|
if($x>10)
|
||||||
|
$this->completed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while($this->completed);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
91
app/Services/PurchaseOrder/CreateInvitations.php
Normal file
91
app/Services/PurchaseOrder/CreateInvitations.php
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace App\Services\PurchaseOrder;
|
||||||
|
|
||||||
|
|
||||||
|
use App\Factory\PurchaseOrderInvitationFactory;
|
||||||
|
use App\Models\PurchaseOrder;
|
||||||
|
use App\Models\PurchaseOrderInvitation;
|
||||||
|
use App\Services\AbstractService;
|
||||||
|
use App\Utils\Traits\MakesHash;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class CreateInvitations extends AbstractService
|
||||||
|
{
|
||||||
|
use MakesHash;
|
||||||
|
|
||||||
|
public PurchaseOrder $purchase_order;
|
||||||
|
|
||||||
|
public function __construct(PurchaseOrder $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()
|
||||||
|
{
|
||||||
|
$contacts = $this->purchase_order->vendor->contacts;
|
||||||
|
|
||||||
|
if($contacts->count() == 0){
|
||||||
|
$this->createBlankContact();
|
||||||
|
|
||||||
|
$this->purchase_order->refresh();
|
||||||
|
$contacts = $this->purchase_order->vendor->contacts;
|
||||||
|
}
|
||||||
|
|
||||||
|
$contacts->each(function ($contact) {
|
||||||
|
$invitation = PurchaseOrderInvitation::whereCompanyId($this->purchase_order->company_id)
|
||||||
|
->whereClientContactId($contact->id)
|
||||||
|
->whereCreditId($this->purchase_order->id)
|
||||||
|
->withTrashed()
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (! $invitation) {
|
||||||
|
$ii = PurchaseOrderInvitation::create($this->purchase_order->company_id, $this->purchase_order->user_id);
|
||||||
|
$ii->key = $this->createDbHash($this->purchase_order->company->db);
|
||||||
|
$ii->purchase_order_id = $this->purchase_order->id;
|
||||||
|
$ii->vendor_contact_id = $contact->id;
|
||||||
|
$ii->save();
|
||||||
|
} elseif (! $contact->send_email) {
|
||||||
|
$invitation->delete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if($this->purchase_order->invitations()->count() == 0) {
|
||||||
|
|
||||||
|
if($contacts->count() == 0){
|
||||||
|
$contact = $this->createBlankContact();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$contact = $contacts->first();
|
||||||
|
|
||||||
|
$invitation = PurchaseOrder::where('company_id', $this->purchase_order->company_id)
|
||||||
|
->where('vendor_contact_id', $contact->id)
|
||||||
|
->where('purchase_order_id', $this->purchase_order->id)
|
||||||
|
->withTrashed()
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if($invitation){
|
||||||
|
$invitation->restore();
|
||||||
|
return $this->purchase_order;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$ii = PurchaseOrderInvitation::create($this->purchase_order->company_id, $this->purchase_order->user_id);
|
||||||
|
$ii->key = $this->createDbHash($this->purchase_order->company->db);
|
||||||
|
$ii->purchase_order_id = $this->purchase_order->id;
|
||||||
|
$ii->vendor_contact_id = $contact->id;
|
||||||
|
$ii->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return $this->purchase_order;
|
||||||
|
}
|
||||||
|
}
|
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 $client;
|
||||||
|
|
||||||
|
private $purchase_order;
|
||||||
|
|
||||||
|
public function __construct($client, $purchase_order)
|
||||||
|
{
|
||||||
|
$this->client = $client;
|
||||||
|
$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;
|
||||||
|
}
|
||||||
|
}
|
@ -52,6 +52,30 @@ class PurchaseOrderService
|
|||||||
$this->purchase_order->public_notes = $this->purchase_order->client->public_notes;
|
$this->purchase_order->public_notes = $this->purchase_order->client->public_notes;
|
||||||
|
|
||||||
|
|
||||||
|
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->client, $this->purchase_order))->run();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Applies the purchase order number.
|
||||||
|
* @return $this PurchaseOrderService object
|
||||||
|
*/
|
||||||
|
public function applyNumber()
|
||||||
|
{
|
||||||
|
$this->purchase_order = (new ApplyNumber($this->purchase_order->client, $this->purchase_order))->run();
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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 [
|
||||||
@ -30,7 +42,6 @@ class PurchaseOrderTransformer extends EntityTransformer
|
|||||||
'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,
|
||||||
|
@ -18,6 +18,7 @@ use App\Models\Expense;
|
|||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Models\Payment;
|
use App\Models\Payment;
|
||||||
use App\Models\Project;
|
use App\Models\Project;
|
||||||
|
use App\Models\PurchaseOrder;
|
||||||
use App\Models\Quote;
|
use App\Models\Quote;
|
||||||
use App\Models\RecurringExpense;
|
use App\Models\RecurringExpense;
|
||||||
use App\Models\RecurringInvoice;
|
use App\Models\RecurringInvoice;
|
||||||
@ -153,6 +154,9 @@ trait GeneratesCounter
|
|||||||
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;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return 'default_number_counter';
|
return 'default_number_counter';
|
||||||
@ -188,6 +192,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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -558,6 +576,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();
|
||||||
@ -622,6 +641,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();
|
||||||
|
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),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -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');
|
||||||
|
}
|
||||||
|
}
|
@ -37,6 +37,7 @@ class PurchaseOrderTest extends TestCase
|
|||||||
Model::reguard();
|
Model::reguard();
|
||||||
|
|
||||||
$this->makeTestData();
|
$this->makeTestData();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPurchaseOrderRest()
|
public function testPurchaseOrderRest()
|
||||||
@ -55,7 +56,7 @@ class PurchaseOrderTest extends TestCase
|
|||||||
|
|
||||||
$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,6 +92,7 @@ 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([
|
||||||
@ -100,11 +102,11 @@ class PurchaseOrderTest extends TestCase
|
|||||||
|
|
||||||
$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',
|
||||||
|
@ -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