This commit is contained in:
David Bomba 2024-11-15 18:41:00 +11:00
parent f510d1a9b5
commit 583ed3eefb
11 changed files with 433 additions and 350 deletions

View File

@ -1 +1 @@
5.10.51
5.10.52

View File

@ -38,17 +38,17 @@ class InvoiceSum
public $total_taxes = 0;
private $total;
private $total = 0;
private $total_discount;
private $total_discount = 0;
private $total_custom_values;
private $total_tax_map;
private $sub_total;
private $sub_total = 0;
private $gross_sub_total;
private $gross_sub_total = 0;
private $precision;

View File

@ -36,17 +36,17 @@ class InvoiceSumInclusive
public $invoice_item;
public $total_taxes;
public $total_taxes = 0;
private $total;
private $total = 0;
private $total_discount;
private $total_discount = 0;
private $total_custom_values;
private $total_tax_map;
private $sub_total;
private $sub_total = 0;
private $precision;

View File

@ -582,6 +582,21 @@ class InvoiceController extends BaseController
return $this->listResponse(Invoice::withTrashed()->whereIn('id', $this->transformKeys($ids))->company());
}
if(in_array($action, ['email','send_email'])) {
$invoice = $invoices->first();
if($user->can('edit', $invoice)){
$template = $request->input('email_type', $invoice->calculateTemplate('invoice'));
BulkInvoiceJob::dispatch($invoices->pluck('id')->toArray(), $user->company()->db, $template);
}
return $this->listResponse(Invoice::withTrashed()->whereIn('id', $this->transformKeys($ids))->company());
}
/*
* Send the other actions to the switch
*/
@ -752,19 +767,6 @@ class InvoiceController extends BaseController
}
break;
case 'email':
case 'send_email':
//check query parameter for email_type and set the template else use calculateTemplate
$template = request()->has('email_type') ? request()->input('email_type') : $invoice->calculateTemplate('invoice');
BulkInvoiceJob::dispatch($invoice, $template);
if (! $bulk) {
return response()->json(['message' => 'email sent'], 200);
}
break;
default:
return response()->json(['message' => ctrans('texts.action_unavailable', ['action' => $action])], 400);
}

View File

@ -16,6 +16,7 @@ use App\Models\Webhook;
use App\Services\Email\Email;
use Illuminate\Bus\Queueable;
use App\Jobs\Entity\EmailEntity;
use App\Libraries\MultiDB;
use App\Services\Email\EmailObject;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
@ -29,6 +30,10 @@ class BulkInvoiceJob implements ShouldQueue
use Queueable;
use SerializesModels;
public $tries = 1;
public $timeout = 3600;
private array $templates = [
'email_template_invoice',
'email_template_quote',
@ -46,7 +51,7 @@ class BulkInvoiceJob implements ShouldQueue
'email_template_purchase_order',
];
public function __construct(public Invoice $invoice, public string $reminder_template){}
public function __construct(public array $invoice_ids, public string $db, public string $reminder_template){}
/**
* Execute the job.
@ -55,13 +60,24 @@ class BulkInvoiceJob implements ShouldQueue
* @return void
*/
public function handle()
{ //only the reminder should mark the reminder sent field
{
MultiDB::setDb($this->db);
$this->invoice->service()->markSent()->save();
Invoice::with([
'invitations',
'invitations.contact.client.country',
'invitations.invoice.client.country',
'invitations.invoice.company'
])
->withTrashed()
->whereIn('id', $this->invoice_ids)
->cursor()
->each(function ($invoice){
$this->invoice->invitations->load('contact.client.country', 'invoice.client.country', 'invoice.company')->each(function ($invitation) {
$invoice->service()->markSent()->save();
$invoice->invitations->each(function ($invitation) {
//@refactor 2024-11-10 - move email into EmailObject/Email::class
$template = $this->resolveTemplateString($this->reminder_template);
$mo = new EmailObject();
@ -75,14 +91,17 @@ class BulkInvoiceJob implements ShouldQueue
$mo->client_id = $invitation->contact->client_id ?? null;
$mo->vendor_id = $invitation->contact->vendor_id ?? null;
Email::dispatch($mo, $invitation->company);
Email::dispatch($mo, $invitation->company->withoutRelations());
});
if ($this->invoice->invitations->count() >= 1) {
$this->invoice->entityEmailEvent($this->invoice->invitations->first(), 'invoice', $this->reminder_template);
$this->invoice->sendEvent(Webhook::EVENT_SENT_INVOICE, "client");
if ($invoice->invitations->count() >= 1) {
$invoice->entityEmailEvent($invoice->invitations->first(), 'invoice', $this->reminder_template);
$invoice->sendEvent(Webhook::EVENT_SENT_INVOICE, "client");
}
sleep(1); // this is needed to slow down the amount of data that is pushed into cache
});
}
private function resolveTemplateString(string $template): string

View File

@ -53,8 +53,15 @@ class StorecoveAdapter
private string $nexus;
private bool $has_error = false;
public function validate(): self
{
if ($this->has_error) {
return $this;
}
return $this;
}
@ -97,14 +104,6 @@ class StorecoveAdapter
$storecove_object = $serializer->normalize($obj, null, [\Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer::SKIP_NULL_VALUES => true]);
// return $storecove_object;
// $storecove_object = $serializer->encode($storecove_object, 'json', $context);
// return $storecove_object;
// return $data;
// $object = $serializer->denormalize(json_encode($storecove_object['document']['invoice']), \App\Services\EDocument\Gateway\Storecove\Models\Invoice::class, 'json', [\Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer::SKIP_NULL_VALUES => true]);
// return $storecove_object;
return $serializer->deserialize(json_encode($storecove_object), \App\Services\EDocument\Gateway\Storecove\Models\Invoice::class, 'json', $context);
}
@ -117,6 +116,7 @@ class StorecoveAdapter
*/
public function transform($invoice): self
{
try {
$this->ninja_invoice = $invoice;
$serializer = $this->getSerializer();
@ -137,6 +137,12 @@ class StorecoveAdapter
$this->storecove_invoice = $serializer->deserialize($peppolInvoice, $parent, 'json', $context);
$this->buildNexus();
}
catch(\Throwable $th){
$this->addError($th->getMessage());
$this->has_error = true;
}
return $this;
@ -149,6 +155,9 @@ class StorecoveAdapter
public function decorate(): self
{
if($this->has_error)
return $this;
//set all taxmap countries - resolve the taxing country
$lines = $this->storecove_invoice->getInvoiceLines();
@ -298,6 +307,11 @@ class StorecoveAdapter
*/
public function getDocument(): mixed
{
if($this->has_error)
return ['errors' => $this->getErrors(), 'document' => false];
$serializer = $this->getSerializer();
$context = [

View File

@ -155,6 +155,8 @@ class Peppol extends AbstractService
private string $tax_category_id;
private array $errors = [];
public function __construct(public Invoice $invoice)
{
$this->company = $invoice->company;
@ -171,6 +173,7 @@ class Peppol extends AbstractService
*/
public function run(): self
{
try {
$this->getJurisdiction(); //Sets the nexus object into the Peppol document.
$this->getAllUsedTaxes(); //Maps all used line item taxes
@ -228,12 +231,10 @@ class Peppol extends AbstractService
->receiverSpecificLevelMutators()
->getPeppol();
//** @todo double check this logic, this will only ever write the doc once */
// if(is_null($this->invoice->backup))
// {
// $this->invoice->e_invoice = $this->toObject();
// $this->invoice->save();
// }
}catch(\Throwable $th) {
nlog("Unable to create Peppol Invoice" . $th->getMessage());
$this->errors[] = $th->getMessage();
}
return $this;
@ -1451,4 +1452,9 @@ class Peppol extends AbstractService
return '0037';
}
public function getErrors(): array
{
return $this->errors;
}
}

View File

@ -26,6 +26,39 @@ use XSLTProcessor;
class EntityLevel
{
private array $eu_country_codes = [
'AT', // Austria
'BE', // Belgium
'BG', // Bulgaria
'CY', // Cyprus
'CZ', // Czech Republic
'DE', // Germany
'DK', // Denmark
'EE', // Estonia
'ES', // Spain
'ES-CN', // Canary Islands
'ES-CE', // Ceuta
'ES-ML', // Melilla
'FI', // Finland
'FR', // France
'GR', // Greece
'HR', // Croatia
'HU', // Hungary
'IE', // Ireland
'IT', // Italy
'LT', // Lithuania
'LU', // Luxembourg
'LV', // Latvia
'MT', // Malta
'NL', // Netherlands
'PL', // Poland
'PT', // Portugal
'RO', // Romania
'SE', // Sweden
'SI', // Slovenia
'SK', // Slovakia
];
private array $client_fields = [
'address1',
'city',
@ -110,10 +143,22 @@ class EntityLevel
try{
$xml = $p->run()->toXml();
if(count($p->getErrors()) >= 1){
foreach($p->getErrors() as $error)
{
$this->errors['invoice'][] = ['field' => $error, 'label' => 'error'];
}
}
}
catch(PeppolValidationException $e) {
$this->errors['invoice'] = ['field' => $e->getInvalidField(), 'label' => $e->getInvalidField()];
};
}
catch(\Throwable $th){
}
if($xml){
// Second pass through the XSLT validator
@ -158,8 +203,8 @@ class EntityLevel
}
//If not an individual, you MUST have a VAT number
if (!in_array($client->classification, ['government','individual']) && !$this->validString($client->vat_number)) {
//If not an individual, you MUST have a VAT number if you are in the EU
if (!in_array($client->classification, ['government', 'individual']) && in_array($client->country->iso_3166_2, $this->eu_country_codes) && !$this->validString($client->vat_number)) {
$errors[] = ['field' => 'vat_number', 'label' => ctrans("texts.vat_number")];
}

View File

@ -90,7 +90,7 @@
"predis/predis": "^2",
"psr/http-message": "^1.0",
"pusher/pusher-php-server": "^7.2",
"quickbooks/v3-php-sdk": "6.1.4-alpha",
"quickbooks/v3-php-sdk": "^6.1",
"razorpay/razorpay": "2.*",
"sentry/sentry-laravel": "^4",
"setasign/fpdf": "^1.8",
@ -218,10 +218,6 @@
{
"type": "vcs",
"url": "https://github.com/turbo124/snappdf"
},
{
"type": "vcs",
"url": "https://github.com/karneaud/QuickBooks-V3-PHP-SDK.git"
}
],
"minimum-stability": "dev",

431
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -17,8 +17,8 @@ return [
'require_https' => env('REQUIRE_HTTPS', true),
'app_url' => rtrim(env('APP_URL', ''), '/'),
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
'app_version' => env('APP_VERSION', '5.10.51'),
'app_tag' => env('APP_TAG', '5.10.51'),
'app_version' => env('APP_VERSION', '5.10.52'),
'app_tag' => env('APP_TAG', '5.10.52'),
'minimum_client_version' => '5.0.16',
'terms_version' => '1.0.1',
'api_secret' => env('API_SECRET', false),