mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-04 01:47:32 -05:00 
			
		
		
		
	
						commit
						f2bfca648f
					
				@ -98,11 +98,11 @@ class CompanySettings extends BaseSettings
 | 
			
		||||
    public $expense_number_pattern = ''; //@implemented
 | 
			
		||||
    public $expense_number_counter = 1; //@implemented
 | 
			
		||||
 | 
			
		||||
    public $recurring_expense_number_pattern = ''; 
 | 
			
		||||
    public $recurring_expense_number_counter = 1; 
 | 
			
		||||
    public $recurring_expense_number_pattern = '';
 | 
			
		||||
    public $recurring_expense_number_counter = 1;
 | 
			
		||||
 | 
			
		||||
    public $recurring_quote_number_pattern = ''; 
 | 
			
		||||
    public $recurring_quote_number_counter = 1; 
 | 
			
		||||
    public $recurring_quote_number_pattern = '';
 | 
			
		||||
    public $recurring_quote_number_counter = 1;
 | 
			
		||||
 | 
			
		||||
    public $vendor_number_pattern = ''; //@implemented
 | 
			
		||||
    public $vendor_number_counter = 1; //@implemented
 | 
			
		||||
@ -276,6 +276,9 @@ class CompanySettings extends BaseSettings
 | 
			
		||||
    public $email_from_name = '';
 | 
			
		||||
    public $auto_archive_invoice_cancelled = false;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public $purchase_order_number_counter = 1; //TODO
 | 
			
		||||
 | 
			
		||||
    public static $casts = [
 | 
			
		||||
        'page_numbering_alignment'           => 'string',
 | 
			
		||||
        'page_numbering'                     => 'bool',
 | 
			
		||||
@ -474,6 +477,7 @@ class CompanySettings extends BaseSettings
 | 
			
		||||
        'portal_custom_footer'               => 'string',
 | 
			
		||||
        'portal_custom_js'                   => 'string',
 | 
			
		||||
        'client_portal_enable_uploads'       => 'bool',
 | 
			
		||||
        'purchase_order_number_counter'      => 'integer',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    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\InvoiceInvitation;
 | 
			
		||||
use App\Models\Payment;
 | 
			
		||||
use App\Models\PurchaseOrderInvitation;
 | 
			
		||||
use App\Models\QuoteInvitation;
 | 
			
		||||
use App\Services\ClientPortal\InstantPayment;
 | 
			
		||||
use App\Utils\CurlUtils;
 | 
			
		||||
@ -41,7 +42,7 @@ class InvitationController extends Controller
 | 
			
		||||
    use MakesDates;
 | 
			
		||||
 | 
			
		||||
    public function router(string $entity, string $invitation_key)
 | 
			
		||||
    {   
 | 
			
		||||
    {
 | 
			
		||||
        Auth::logout();
 | 
			
		||||
 | 
			
		||||
        return $this->genericRouter($entity, $invitation_key);
 | 
			
		||||
@ -166,7 +167,7 @@ class InvitationController extends Controller
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        set_time_limit(45);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        if(Ninja::isHosted())
 | 
			
		||||
            return $this->returnRawPdf($entity, $invitation_key);
 | 
			
		||||
 | 
			
		||||
@ -202,7 +203,7 @@ class InvitationController extends Controller
 | 
			
		||||
        return response()->streamDownload(function () use($file) {
 | 
			
		||||
                echo $file;
 | 
			
		||||
        },  $file_name, $headers);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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)
 | 
			
		||||
                                    ->with('contact.client')
 | 
			
		||||
                                    ->firstOrFail();
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        auth()->guard('contact')->loginUsingId($invitation->contact->id, true);
 | 
			
		||||
 | 
			
		||||
        $invoice = $invitation->invoice;
 | 
			
		||||
 | 
			
		||||
        if($invoice->partial > 0)
 | 
			
		||||
            $amount = round($invoice->partial, (int)$invoice->client->currency()->precision);
 | 
			
		||||
        else 
 | 
			
		||||
        else
 | 
			
		||||
            $amount = round($invoice->balance, (int)$invoice->client->currency()->precision);
 | 
			
		||||
 | 
			
		||||
        $gateways = $invitation->contact->client->service()->getPaymentMethods($amount);
 | 
			
		||||
@ -279,6 +280,10 @@ class InvitationController extends Controller
 | 
			
		||||
            $invite = CreditInvitation::withTrashed()->where('key', $invitation_key)->first();
 | 
			
		||||
            $invite->contact->send_email = false;
 | 
			
		||||
            $invite->contact->save();
 | 
			
		||||
        }elseif($entity == 'purchase_order'){
 | 
			
		||||
            $invite = PurchaseOrderInvitation::withTrashed()->where('key', $invitation_key)->first();
 | 
			
		||||
            $invite->contact->send_email = false;
 | 
			
		||||
            $invite->contact->save();
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
            return abort(404);
 | 
			
		||||
 | 
			
		||||
@ -12,8 +12,12 @@
 | 
			
		||||
namespace App\Models;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
use App\Jobs\Entity\CreateEntityPdf;
 | 
			
		||||
use App\Services\PurchaseOrder\PurchaseOrderService;
 | 
			
		||||
use App\Utils\Ninja;
 | 
			
		||||
use Illuminate\Database\Eloquent\SoftDeletes;
 | 
			
		||||
use Illuminate\Support\Carbon;
 | 
			
		||||
use Illuminate\Support\Facades\Storage;
 | 
			
		||||
 | 
			
		||||
class PurchaseOrder extends BaseModel
 | 
			
		||||
{
 | 
			
		||||
@ -129,11 +133,52 @@ class PurchaseOrder extends BaseModel
 | 
			
		||||
    {
 | 
			
		||||
        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()
 | 
			
		||||
    {
 | 
			
		||||
        return $this->hasMany(CreditInvitation::class);
 | 
			
		||||
        return $this->hasMany(PurchaseOrderInvitation::class);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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()
 | 
			
		||||
            ->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\PaymentWasUpdated;
 | 
			
		||||
use App\Events\Payment\PaymentWasVoided;
 | 
			
		||||
use App\Events\PurchaseOrder\PurchaseOrderWasMarkedSent;
 | 
			
		||||
use App\Events\Quote\QuoteWasApproved;
 | 
			
		||||
use App\Events\Quote\QuoteWasArchived;
 | 
			
		||||
use App\Events\Quote\QuoteWasCreated;
 | 
			
		||||
@ -558,6 +559,8 @@ class EventServiceProvider extends ServiceProvider
 | 
			
		||||
        VendorWasUpdated::class => [
 | 
			
		||||
            VendorUpdatedActivity::class,
 | 
			
		||||
        ],
 | 
			
		||||
        PurchaseOrderWasMarkedSent::class => [
 | 
			
		||||
        ],
 | 
			
		||||
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@ namespace App\Repositories;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
use App\Models\PurchaseOrder;
 | 
			
		||||
use App\Models\PurchaseOrderInvitation;
 | 
			
		||||
use App\Utils\Traits\MakesHash;
 | 
			
		||||
 | 
			
		||||
class PurchaseOrderRepository extends BaseRepository
 | 
			
		||||
@ -30,5 +31,9 @@ class PurchaseOrderRepository extends BaseRepository
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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\PurchaseOrderInvitation;
 | 
			
		||||
use App\Utils\Traits\MakesHash;
 | 
			
		||||
 | 
			
		||||
class PurchaseOrderTransformer extends EntityTransformer
 | 
			
		||||
{
 | 
			
		||||
    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)
 | 
			
		||||
    {
 | 
			
		||||
        return [
 | 
			
		||||
@ -26,19 +38,18 @@ class PurchaseOrderTransformer extends EntityTransformer
 | 
			
		||||
            'user_id' => $this->encodePrimaryKey($purchase_order->user_id),
 | 
			
		||||
            'project_id' => $this->encodePrimaryKey($purchase_order->project_id),
 | 
			
		||||
            'assigned_user_id' => $this->encodePrimaryKey($purchase_order->assigned_user_id),
 | 
			
		||||
            'vendor_id' => (string) $this->encodePrimaryKey($purchase_order->vendor_id),
 | 
			
		||||
            'amount' => (float) $purchase_order->amount,
 | 
			
		||||
            'balance' => (float) $purchase_order->balance,
 | 
			
		||||
            '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),
 | 
			
		||||
            'design_id' => (string) $this->encodePrimaryKey($purchase_order->design_id),
 | 
			
		||||
            'created_at' => (int) $purchase_order->created_at,
 | 
			
		||||
            'updated_at' => (int) $purchase_order->updated_at,
 | 
			
		||||
            'archived_at' => (int) $purchase_order->deleted_at,
 | 
			
		||||
            'is_deleted' => (bool) $purchase_order->is_deleted,
 | 
			
		||||
            'vendor_id' => (string)$this->encodePrimaryKey($purchase_order->vendor_id),
 | 
			
		||||
            'amount' => (float)$purchase_order->amount,
 | 
			
		||||
            'balance' => (float)$purchase_order->balance,
 | 
			
		||||
            'client_id' => (string)$this->encodePrimaryKey($purchase_order->client_id),
 | 
			
		||||
            'status_id' => (string)($purchase_order->status_id ?: 1),
 | 
			
		||||
            'design_id' => (string)$this->encodePrimaryKey($purchase_order->design_id),
 | 
			
		||||
            'created_at' => (int)$purchase_order->created_at,
 | 
			
		||||
            'updated_at' => (int)$purchase_order->updated_at,
 | 
			
		||||
            'archived_at' => (int)$purchase_order->deleted_at,
 | 
			
		||||
            'is_deleted' => (bool)$purchase_order->is_deleted,
 | 
			
		||||
            'number' => $purchase_order->number ?: '',
 | 
			
		||||
            'discount' => (float) $purchase_order->discount,
 | 
			
		||||
            'discount' => (float)$purchase_order->discount,
 | 
			
		||||
            'po_number' => $purchase_order->po_number ?: '',
 | 
			
		||||
            'date' => $purchase_order->date ?: '',
 | 
			
		||||
            'last_sent_date' => $purchase_order->last_sent_date ?: '',
 | 
			
		||||
@ -51,36 +62,36 @@ class PurchaseOrderTransformer extends EntityTransformer
 | 
			
		||||
            'terms' => $purchase_order->terms ?: '',
 | 
			
		||||
            'public_notes' => $purchase_order->public_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_rate1' => (float) $purchase_order->tax_rate1,
 | 
			
		||||
            'tax_rate1' => (float)$purchase_order->tax_rate1,
 | 
			
		||||
            '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_rate3' => (float) $purchase_order->tax_rate3,
 | 
			
		||||
            'total_taxes' => (float) $purchase_order->total_taxes,
 | 
			
		||||
            'is_amount_discount' => (bool) ($purchase_order->is_amount_discount ?: false),
 | 
			
		||||
            'tax_rate3' => (float)$purchase_order->tax_rate3,
 | 
			
		||||
            'total_taxes' => (float)$purchase_order->total_taxes,
 | 
			
		||||
            'is_amount_discount' => (bool)($purchase_order->is_amount_discount ?: false),
 | 
			
		||||
            '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 ?: '',
 | 
			
		||||
            'custom_value1' => (string) $purchase_order->custom_value1 ?: '',
 | 
			
		||||
            'custom_value2' => (string) $purchase_order->custom_value2 ?: '',
 | 
			
		||||
            'custom_value3' => (string) $purchase_order->custom_value3 ?: '',
 | 
			
		||||
            'custom_value4' => (string) $purchase_order->custom_value4 ?: '',
 | 
			
		||||
            'has_tasks' => (bool) $purchase_order->has_tasks,
 | 
			
		||||
            'has_expenses' => (bool) $purchase_order->has_expenses,
 | 
			
		||||
            'custom_surcharge1' => (float) $purchase_order->custom_surcharge1,
 | 
			
		||||
            'custom_surcharge2' => (float) $purchase_order->custom_surcharge2,
 | 
			
		||||
            'custom_surcharge3' => (float) $purchase_order->custom_surcharge3,
 | 
			
		||||
            'custom_surcharge4' => (float) $purchase_order->custom_surcharge4,
 | 
			
		||||
            'custom_surcharge_tax1' => (bool) $purchase_order->custom_surcharge_tax1,
 | 
			
		||||
            'custom_surcharge_tax2' => (bool) $purchase_order->custom_surcharge_tax2,
 | 
			
		||||
            'custom_surcharge_tax3' => (bool) $purchase_order->custom_surcharge_tax3,
 | 
			
		||||
            'custom_surcharge_tax4' => (bool) $purchase_order->custom_surcharge_tax4,
 | 
			
		||||
            'line_items' => $purchase_order->line_items ?: (array) [],
 | 
			
		||||
            'custom_value1' => (string)$purchase_order->custom_value1 ?: '',
 | 
			
		||||
            'custom_value2' => (string)$purchase_order->custom_value2 ?: '',
 | 
			
		||||
            'custom_value3' => (string)$purchase_order->custom_value3 ?: '',
 | 
			
		||||
            'custom_value4' => (string)$purchase_order->custom_value4 ?: '',
 | 
			
		||||
            'has_tasks' => (bool)$purchase_order->has_tasks,
 | 
			
		||||
            'has_expenses' => (bool)$purchase_order->has_expenses,
 | 
			
		||||
            'custom_surcharge1' => (float)$purchase_order->custom_surcharge1,
 | 
			
		||||
            'custom_surcharge2' => (float)$purchase_order->custom_surcharge2,
 | 
			
		||||
            'custom_surcharge3' => (float)$purchase_order->custom_surcharge3,
 | 
			
		||||
            'custom_surcharge4' => (float)$purchase_order->custom_surcharge4,
 | 
			
		||||
            'custom_surcharge_tax1' => (bool)$purchase_order->custom_surcharge_tax1,
 | 
			
		||||
            'custom_surcharge_tax2' => (bool)$purchase_order->custom_surcharge_tax2,
 | 
			
		||||
            'custom_surcharge_tax3' => (bool)$purchase_order->custom_surcharge_tax3,
 | 
			
		||||
            'custom_surcharge_tax4' => (bool)$purchase_order->custom_surcharge_tax4,
 | 
			
		||||
            'line_items' => $purchase_order->line_items ?: (array)[],
 | 
			
		||||
            'entity_type' => 'credit',
 | 
			
		||||
            'exchange_rate' => (float) $purchase_order->exchange_rate,
 | 
			
		||||
            'paid_to_date' => (float) $purchase_order->paid_to_date,
 | 
			
		||||
            'exchange_rate' => (float)$purchase_order->exchange_rate,
 | 
			
		||||
            'paid_to_date' => (float)$purchase_order->paid_to_date,
 | 
			
		||||
            'subscription_id' => $this->encodePrimaryKey($purchase_order->subscription_id),
 | 
			
		||||
        ];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@ use App\Models\Expense;
 | 
			
		||||
use App\Models\Invoice;
 | 
			
		||||
use App\Models\Payment;
 | 
			
		||||
use App\Models\Project;
 | 
			
		||||
use App\Models\PurchaseOrder;
 | 
			
		||||
use App\Models\Quote;
 | 
			
		||||
use App\Models\RecurringExpense;
 | 
			
		||||
use App\Models\RecurringInvoice;
 | 
			
		||||
@ -44,8 +45,8 @@ trait GeneratesCounter
 | 
			
		||||
 | 
			
		||||
        $is_client_counter = false;
 | 
			
		||||
 | 
			
		||||
        $counter_string = $this->getEntityCounter($entity, $client);  
 | 
			
		||||
        $pattern = $this->getNumberPattern($entity, $client);  
 | 
			
		||||
        $counter_string = $this->getEntityCounter($entity, $client);
 | 
			
		||||
        $pattern = $this->getNumberPattern($entity, $client);
 | 
			
		||||
 | 
			
		||||
        if ((strpos($pattern, 'clientCounter') !== false) || (strpos($pattern, 'client_counter') !==false) ) {
 | 
			
		||||
 | 
			
		||||
@ -71,9 +72,9 @@ trait GeneratesCounter
 | 
			
		||||
            $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);
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        if(strlen($pattern) > 1 && (stripos($pattern, 'counter') === false)){
 | 
			
		||||
            $pattern = $pattern.'{$counter}';
 | 
			
		||||
        }
 | 
			
		||||
@ -127,9 +128,9 @@ trait GeneratesCounter
 | 
			
		||||
                break;
 | 
			
		||||
            case Quote::class:
 | 
			
		||||
 | 
			
		||||
                if ($this->hasSharedCounter($client, 'quote')) 
 | 
			
		||||
                if ($this->hasSharedCounter($client, 'quote'))
 | 
			
		||||
                    return 'invoice_number_counter';
 | 
			
		||||
                
 | 
			
		||||
 | 
			
		||||
                return 'quote_number_counter';
 | 
			
		||||
                break;
 | 
			
		||||
            case RecurringInvoice::class:
 | 
			
		||||
@ -145,14 +146,17 @@ trait GeneratesCounter
 | 
			
		||||
                return 'payment_number_counter';
 | 
			
		||||
                break;
 | 
			
		||||
            case Credit::class:
 | 
			
		||||
                if ($this->hasSharedCounter($client, 'credit')) 
 | 
			
		||||
                if ($this->hasSharedCounter($client, 'credit'))
 | 
			
		||||
                    return 'invoice_number_counter';
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
                return 'credit_number_counter';
 | 
			
		||||
                break;
 | 
			
		||||
            case Project::class:
 | 
			
		||||
                return 'project_number_counter';
 | 
			
		||||
                break;
 | 
			
		||||
            case PurchaseOrder::class:
 | 
			
		||||
                return 'purchase_order_number_counter';
 | 
			
		||||
                break;
 | 
			
		||||
 | 
			
		||||
            default:
 | 
			
		||||
                return 'default_number_counter';
 | 
			
		||||
@ -188,6 +192,20 @@ trait GeneratesCounter
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -385,7 +403,7 @@ trait GeneratesCounter
 | 
			
		||||
     *
 | 
			
		||||
     * @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')
 | 
			
		||||
            return (bool) $client->getSetting('shared_invoice_quote_counter');
 | 
			
		||||
@ -438,9 +456,9 @@ trait GeneratesCounter
 | 
			
		||||
    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 true;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
@ -504,7 +522,7 @@ trait GeneratesCounter
 | 
			
		||||
 | 
			
		||||
        if($reset_counter_frequency == 0)
 | 
			
		||||
            return;
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $timezone = Timezone::find($client->getSetting('timezone_id'));
 | 
			
		||||
 | 
			
		||||
        $reset_date = Carbon::parse($client->getSetting('reset_counter_date'), $timezone->name);
 | 
			
		||||
@ -558,6 +576,7 @@ trait GeneratesCounter
 | 
			
		||||
        $settings->invoice_number_counter = 1;
 | 
			
		||||
        $settings->quote_number_counter = 1;
 | 
			
		||||
        $settings->credit_number_counter = 1;
 | 
			
		||||
        $settings->purchase_order_number_counter = 1;
 | 
			
		||||
 | 
			
		||||
        $client->company->settings = $settings;
 | 
			
		||||
        $client->company->save();
 | 
			
		||||
@ -622,6 +641,7 @@ trait GeneratesCounter
 | 
			
		||||
        $settings->task_number_counter = 1;
 | 
			
		||||
        $settings->expense_number_counter = 1;
 | 
			
		||||
        $settings->recurring_expense_number_counter =1;
 | 
			
		||||
        $settings->purchase_order_number_counter = 1;
 | 
			
		||||
 | 
			
		||||
        $company->settings = $settings;
 | 
			
		||||
        $company->save();
 | 
			
		||||
@ -644,7 +664,7 @@ trait GeneratesCounter
 | 
			
		||||
 | 
			
		||||
        $search = [];
 | 
			
		||||
        $replace = [];
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        $search[] = '{$counter}';
 | 
			
		||||
        $replace[] = $counter;
 | 
			
		||||
 | 
			
		||||
@ -659,7 +679,7 @@ trait GeneratesCounter
 | 
			
		||||
 | 
			
		||||
        $search[] = '{$year}';
 | 
			
		||||
        $replace[] = Carbon::now($entity->company->timezone()->name)->format('Y');
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        if (strstr($pattern, '{$user_id}') || strstr($pattern, '{$userId}')) {
 | 
			
		||||
            $user_id = $entity->user_id ? $entity->user_id : 0;
 | 
			
		||||
            $search[] = '{$user_id}';
 | 
			
		||||
@ -683,7 +703,7 @@ trait GeneratesCounter
 | 
			
		||||
            $search[] = '{$vendor_id_number}';
 | 
			
		||||
            $replace[] = $entity->id_number;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        if ($entity instanceof Expense) {
 | 
			
		||||
            if ($entity->vendor) {
 | 
			
		||||
                $search[] = '{$vendor_id_number}';
 | 
			
		||||
@ -708,7 +728,7 @@ trait GeneratesCounter
 | 
			
		||||
            $search[] = '{$expense_id_number}';
 | 
			
		||||
            $replace[] = $entity->id_number;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        if ($entity->client || ($entity instanceof Client)) {
 | 
			
		||||
            $client = $entity->client ?: $entity;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										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();
 | 
			
		||||
 | 
			
		||||
        $this->makeTestData();
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testPurchaseOrderRest()
 | 
			
		||||
@ -44,18 +45,18 @@ class PurchaseOrderTest extends TestCase
 | 
			
		||||
        $response = $this->withHeaders([
 | 
			
		||||
            'X-API-SECRET' => config('ninja.api_secret'),
 | 
			
		||||
            '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 = $this->withHeaders([
 | 
			
		||||
            'X-API-SECRET' => config('ninja.api_secret'),
 | 
			
		||||
            '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);
 | 
			
		||||
 | 
			
		||||
        $credit_update = [
 | 
			
		||||
        $purchase_order_update = [
 | 
			
		||||
            'tax_name1' => 'dippy',
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
@ -64,14 +65,14 @@ class PurchaseOrderTest extends TestCase
 | 
			
		||||
        $response = $this->withHeaders([
 | 
			
		||||
            'X-API-SECRET' => config('ninja.api_secret'),
 | 
			
		||||
            '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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testPostNewPurchaseOrder()
 | 
			
		||||
    {
 | 
			
		||||
        $purchase_order = [
 | 
			
		||||
            'status_id' => 1,
 | 
			
		||||
            'number' => 'dfdfd',
 | 
			
		||||
            'discount' => 0,
 | 
			
		||||
            'is_amount_discount' => 1,
 | 
			
		||||
            'number' => '34343xx43',
 | 
			
		||||
@ -91,20 +92,21 @@ class PurchaseOrderTest extends TestCase
 | 
			
		||||
        ])->post('/api/v1/purchase_orders/', $purchase_order)
 | 
			
		||||
            ->assertStatus(200);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testPurchaseOrderDelete()
 | 
			
		||||
    {
 | 
			
		||||
        $response = $this->withHeaders([
 | 
			
		||||
            'X-API-SECRET' => config('ninja.api_secret'),
 | 
			
		||||
            '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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function testPurchaseOrderUpdate()
 | 
			
		||||
    {
 | 
			
		||||
        $data = [
 | 
			
		||||
            'status_id' => 1,
 | 
			
		||||
            'number' => 'dfdfd',
 | 
			
		||||
            'discount' => 0,
 | 
			
		||||
            'is_amount_discount' => 1,
 | 
			
		||||
            'number' => '3434343',
 | 
			
		||||
@ -121,14 +123,14 @@ class PurchaseOrderTest extends TestCase
 | 
			
		||||
        $response = $this->withHeaders([
 | 
			
		||||
            'X-API-SECRET' => config('ninja.api_secret'),
 | 
			
		||||
            '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 = $this->withHeaders([
 | 
			
		||||
            'X-API-SECRET' => config('ninja.api_secret'),
 | 
			
		||||
            '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);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -36,6 +36,8 @@ use App\Models\GroupSetting;
 | 
			
		||||
use App\Models\InvoiceInvitation;
 | 
			
		||||
use App\Models\Product;
 | 
			
		||||
use App\Models\Project;
 | 
			
		||||
use App\Models\PurchaseOrder;
 | 
			
		||||
use App\Models\PurchaseOrderInvitation;
 | 
			
		||||
use App\Models\Quote;
 | 
			
		||||
use App\Models\QuoteInvitation;
 | 
			
		||||
use App\Models\RecurringExpense;
 | 
			
		||||
@ -476,6 +478,26 @@ trait MockAccountData
 | 
			
		||||
        $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);
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user