Ingest .xml and attach to entity

This commit is contained in:
David Bomba 2024-11-10 14:47:18 +11:00
parent cdddfa341e
commit a3718170c7
9 changed files with 161 additions and 34 deletions

View File

@ -29,18 +29,39 @@ class InvoiceSyncCast implements CastsAttributes
return null;
}
$is = new InvoiceSync();
$is->qb_id = $data['qb_id'];
$is = new InvoiceSync($data);
// $is->qb_id = $data['qb_id'];
// $is->email = $data['email'];
return $is;
}
public function set($model, string $key, $value, array $attributes)
{
$data = [];
if(isset($value->qb_id) && strlen($value->qb_id) >= 1)
$data['qb_id'] = $value->qb_id;
if (isset($value->email) && $value->email !== null) {
$data['email'] = [
'body' => $value->email->body ?? '',
'subject' => $value->email->subject ?? '',
'template' => $value->email->template ?? '',
'entity' => $value->email->entity ?? '',
'entity_id' => $value->email->entity_id ?? '',
'cc_email' => $value->email->cc_email ?? '',
'action' => $value->email->action ?? '',
'ids' => $value->email->ids ?? '',
'type' => $value->email->email_type ?? '',
];
}
return [
$key => json_encode([
'qb_id' => $value->qb_id,
])
$key => json_encode($data)
];
}
}

View File

@ -21,9 +21,27 @@ class InvoiceSync implements Castable
{
public string $qb_id;
public mixed $email;
public function __construct(array $attributes = [])
{
$this->qb_id = $attributes['qb_id'] ?? '';
$this->email = new \stdClass();
if(isset($attributes['email']))
{
$this->email->body = $attributes['email']['body'] ?? '';
$this->email->subject = $attributes['email']['subject'] ?? '';
$this->email->template = $attributes['email']['template'] ?? '';
$this->email->entity = $attributes['email']['entity'] ?? '';
$this->email->entity_id = $attributes['email']['entity_id'] ?? '';
$this->email->cc_email = $attributes['email']['cc_email'] ?? '';
$this->email->action = $attributes['email']['action'] ?? '';
$this->email->ids = $attributes['email']['ids'] ?? [];
$this->email->email_type = $attributes['email']['type'] ?? '';
}
}
/**

View File

@ -11,6 +11,7 @@
namespace App\Http\Controllers;
use App\DataMapper\InvoiceSync;
use App\Utils\Ninja;
use App\Models\Quote;
use App\Models\Credit;
@ -69,6 +70,28 @@ class EmailController extends BaseController
/** @var \App\Models\User $user */
$user = auth()->user();
$company = $entity_obj->company;
//Only handle Peppol Invoices for now.
if($entity_obj instanceof Invoice && !isset($entity_obj->sync->email)){
// if($entity_obj instanceof Invoice && $company->isPeppolSender()){
$sync = $entity_obj->sync ?? new InvoiceSync();
$sync->email->body = strlen($body) > 3 ? $body : null;
$sync->email->subject = strlen($subject) > 3 ? $subject : null;
$sync->email->template = $request->input('template');
$sync->email->entity = $request->input('entity');
$sync->email->entity_id = $request->input('entity_id');
$sync->email->cc_email = $request->cc_email;
$entity_obj->sync = $sync;
$entity_obj->saveQuietly();
$entity_obj->service()->markSent()->save();
\App\Services\EDocument\Jobs\SendEDocument::dispatch(get_class($entity_obj), $entity_obj->id, $company->db);
nlog($sync);
return;
}
if ($request->cc_email && (Ninja::isSelfHost() || $user->account->isPremium())) {
@ -92,6 +115,19 @@ class EmailController extends BaseController
$entity_obj = $entity_obj->fresh();
$entity_obj->last_sent_date = now();
if(!empty($entity_obj->sync))
{
$sync = $entity_obj->sync;
if (empty($sync->qb_id)) {
$sync = null;
} else {
unset($sync->email);
}
$entity_obj->sync = $sync;
}
$entity_obj->save();
/*Only notify the admin ONCE, not once per contact/invite*/
@ -139,21 +175,7 @@ class EmailController extends BaseController
return $this->itemResponse($entity_obj->fresh());
}
// private function sendPurchaseOrder($entity_obj, $data, $template)
// {
// $this->entity_type = PurchaseOrder::class;
// $this->entity_transformer = PurchaseOrderTransformer::class;
// $data['template'] = $template;
// PurchaseOrderEmail::dispatch($entity_obj, $entity_obj->company, $data);
// $entity_obj->sendEvent(Webhook::EVENT_SENT_PURCHASE_ORDER, "vendor");
// return $this->itemResponse($entity_obj);
// }
private function resolveClass(string $entity): string
private function resolveClass(string $entity): string
{
$class = '';

View File

@ -11,10 +11,13 @@
namespace App\Http\Controllers;
use App\Utils\Ninja;
use App\Models\Company;
use App\Libraries\MultiDB;
use Illuminate\Http\Request;
use App\Utils\Traits\SavesDocuments;
use App\Jobs\Mailgun\ProcessMailgunWebhook;
use App\Http\Requests\Email\SendEmailRequest;
use App\Jobs\Mailgun\ProcessMailgunInboundWebhook;
/**
@ -22,6 +25,8 @@ use App\Jobs\Mailgun\ProcessMailgunInboundWebhook;
*/
class MailgunController extends BaseController
{
use SavesDocuments;
public function __construct()
{
}
@ -120,14 +125,68 @@ class MailgunController extends BaseController
{
$input = $request->all();
$authorizedByHash = \hash_equals(\hash_hmac('sha256', $input['timestamp'] . $input['token'], config('services.mailgun.webhook_signing_key')), $input['signature']);
$authorizedByToken = $request->has('token') && $request->get('token') == config('ninja.inbound_mailbox.inbound_webhook_token');
if (!$authorizedByHash && !$authorizedByToken) {
return response()->json(['message' => 'Unauthorized'], 403);
}
nlog($input);
if($input['recipient'] == config('ninja.storecove_email_catchall') && stripos('no-reply@mailer.storecove.com', $input['from']) !== false){
foreach ($request->files as $file) {
// Process each file
nlog($file);
nlog("checks");
/** Peppol invoice xml ingest */
if(stripos($input['recipient'], "peppol_") !== false && stripos($input['from'], 'no-reply@mailer.storecove.com') !== false){
$email_parts = explode("@", $input['recipient']);
nlog($email_parts);
$parts = explode("_", $email_parts[0]);
nlog($parts);
if(count($parts) != 4 && $parts[0] != 'peppol' && stripos('db-ninja-0', $parts[3]) !== false)
return;
$entity = ucfirst($parts[1]);
$entity_id = $parts[2];
$db = $parts[3];
MultiDB::setDB($db);
$class = "\\App\\Models\\".ucfirst(\Illuminate\Support\Str::camel($entity));
/** @var \App\Models\Invoice $entity */
$entity = $class::query()->withTrashed()->find($entity_id);
if(!$entity){
nlog("could not resolve entity for mailgun webhook");
nlog($input);
return;
}
foreach ($request->files as $file) {
$this->saveDocuments($file, $entity, true);
}
if(empty($entity->sync))
return; //just save the document, do not email it!
$sync = $entity->sync;
$request = new SendEmailRequest([
'entity' => $sync->email->entity,
'entity_id' => $entity->id,
'template' => $sync->email->template,
'subject' => $sync->email->subject,
'body' => $sync->email->body,
'cc_email' => $sync->email->cc_email,
]);
$request->setUserResolver(fn () => $entity->user); // Or auth()->user()
app(\App\Http\Controllers\EmailController::class)->send($request);
return;
}
if (!array_key_exists('sender', $input) || !array_key_exists('recipient', $input) || !array_key_exists('message-url', $input)) {
@ -137,11 +196,7 @@ class MailgunController extends BaseController
// @turbo124 TODO: how to check for services.mailgun.webhook_signing_key on company level, when custom credentials are defined
// TODO: validation for client mail credentials by recipient
$authorizedByHash = \hash_equals(\hash_hmac('sha256', $input['timestamp'] . $input['token'], config('services.mailgun.webhook_signing_key')), $input['signature']);
$authorizedByToken = $request->has('token') && $request->get('token') == config('ninja.inbound_mailbox.inbound_webhook_token');
if (!$authorizedByHash && !$authorizedByToken)
return response()->json(['message' => 'Unauthorized'], 403);
/** @var \App\Models\Company $company */
$company = MultiDB::findAndSetDbByExpenseMailbox($input["recipient"]);

View File

@ -112,7 +112,7 @@ class SendEmailRequest extends Request
public function messages()
{
return [
'template.in' => 'Template :input is anot a valid template.',
'template.in' => 'Template :input is not a valid template.',
'entity.in' => 'Entity :input is not a valid entity.'
];
}

View File

@ -980,4 +980,9 @@ class Company extends BaseModel
return new CompanyService($this);
}
public function isPeppolSender()
{
return Ninja::isHosted() && $this->account->isPaid() && $this->account->isEnterpriseClient() && $this->account->e_invoice_quota > 0 && $this->settings->e_invoice_type == 'PEPPOL' && $this->tax_data->acts_as_sender;
}
}

View File

@ -630,7 +630,7 @@ class Mutator implements MutatorInterface
*/
private function setEmailRouting(string $email): self
{
$email = "peppol@mail.invoicing.co";
$email = "peppol_invoice_{$this->invoice->id}_{$this->invoice->company->db}@mail.invoicing.co";
$meta = $this->getStorecoveMeta();
if(isset($meta['routing']['emails'])) {

View File

@ -1,11 +1,15 @@
@extends('portal.ninja2020.layout.clean')
@section('meta_title', ctrans('texts.login'))
@section('head')
@component('portal.ninja2020.components.test')
<input type="hidden" id="test_email" value="{{ config('ninja.testvars.username') }}">
<input type="hidden" id="test_password" value="{{ config('ninja.testvars.password') }}">
<input type="hidden" id="test_email" value="{{ config('ninja.testvars.username') }}">
<input type="hidden" id="test_password" value="{{ config('ninja.testvars.password') }}">
@endcomponent
@endsection
@section('body')
<div class="grid lg:grid-cols-3 mx-6 md:mx-0">
@if($account && !$account->isPaid())

View File

@ -62,6 +62,8 @@
<title>@yield('meta_title', '')</title>
@endif
@yield('head')
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="@yield('meta_description')"/>