Merge Template Support

This commit is contained in:
David Bomba 2023-11-13 15:23:17 +11:00
commit ba4d31e901
11 changed files with 1426 additions and 110 deletions

View File

@ -388,6 +388,7 @@ class PreviewController extends BaseController
$design_object = json_decode(json_encode(request()->input('design')), 1);
$ts = (new TemplateService());
try {
$ts->setCompany($company)
->setTemplate($design_object)
@ -395,7 +396,6 @@ class PreviewController extends BaseController
} catch(SyntaxError $e) {
// return response()->json(['message' => 'Twig syntax is invalid.', 'errors' => new \stdClass], 422);
}
$html = $ts->getHtml();

View File

@ -54,14 +54,11 @@ class StoreTaskRequest extends Request
$rules['project_id'] = 'bail|required|exists:projects,id,company_id,'.$user->company()->id.',is_deleted,0';
}
$rules['time_log'] = ['bail', function ($attribute, $values, $fail) {
if(is_string($values)) {
$values = json_decode($values, 1);
}
$rules['time_log'] = ['bail',function ($attribute, $values, $fail) {
if(!is_array($values)) {
return $fail('The '.$attribute.' is invalid. Must be an array.');
if(!is_array(json_decode($values, true))) {
$fail('The '.$attribute.' must be a valid array.');
return;
}
foreach ($values as $k) {
@ -119,6 +116,10 @@ class StoreTaskRequest extends Request
}
}
if(!isset($input['time_log']) || empty($input['time_log']) || $input['time_log'] == '{}'){
$input['time_log'] = json_encode([]);
}
$this->replace($input);
}
}

View File

@ -38,12 +38,14 @@ class UpdateTaskRequest extends Request
/** @var \App\Models\User $user */
$user = auth()->user();
/** @var \App\Models\User $user */
$user = auth()->user();
return $user->can('edit', $this->task);
}
public function rules()
{
/** @var \App\Models\User $user */
$user = auth()->user();
@ -61,14 +63,11 @@ class UpdateTaskRequest extends Request
$rules['project_id'] = 'bail|required|exists:projects,id,company_id,'.$user->company()->id.',is_deleted,0';
}
$rules['time_log'] = ['bail',function ($attribute, $values, $fail) {
$rules['time_log'] = ['bail', function ($attribute, $values, $fail) {
if(is_string($values)) {
$values = json_decode($values, 1);
}
if(!is_array($values)) {
return $fail('The '.$attribute.' is invalid. Must be an array.');
if(!is_array(json_decode($values, true))) {
$fail('The '.$attribute.' must be a valid array.');
return;
}
foreach ($values as $k) {
@ -129,6 +128,10 @@ class UpdateTaskRequest extends Request
}
if(!isset($input['time_log']) || empty($input['time_log']) || $input['time_log'] == '{}') {
$input['time_log'] = json_encode([]);
}
$this->replace($input);
}

View File

@ -268,7 +268,8 @@ class PurchaseOrder extends BaseModel
{
return $this->belongsTo(Client::class)->withTrashed();
}
public function markInvitationsSent()
public function markInvitationsSent(): void
{
$this->invitations->each(function ($invitation) {
if (! isset($invitation->sent_date)) {

View File

@ -239,4 +239,23 @@ class Task extends BaseModel
return $this->company->settings->default_task_rate ?? 0;
}
public function processLogs()
{
return
collect($this->time_log)->map(function ($log){
$parent_entity = $this->client ?? $this->company;
if($log[0])
$log[0] = Carbon::createFromTimestamp($log[0])->format($parent_entity->date_format());
if($log[1] && $log[1] != 0)
$log[1] = Carbon::createFromTimestamp($log[1])->format($parent_entity->date_format());
else
$log[1] = ctrans('texts.running');
return $log;
})->toArray();
}
}

View File

@ -1621,12 +1621,6 @@ class PdfBuilder
// Dom Traversal
///////////////////////////////////////
public function getSectionNode(string $selector)
{
return $this->document->getElementById($selector);
}
public function updateElementProperties() :self
{
foreach ($this->sections as $element) {

File diff suppressed because it is too large Load Diff

156
composer.lock generated
View File

@ -485,6 +485,7 @@
},
{
"name": "aws/aws-sdk-php",
<<<<<<< HEAD
"version": "3.285.3",
"source": {
"type": "git",
@ -495,6 +496,18 @@
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/afa1e722f1b2c95644f375dc1a19072e4daf67be",
"reference": "afa1e722f1b2c95644f375dc1a19072e4daf67be",
=======
"version": "3.284.0",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "1b7e926acc990509e3d13fa708cdede686b40d90"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/1b7e926acc990509e3d13fa708cdede686b40d90",
"reference": "1b7e926acc990509e3d13fa708cdede686b40d90",
>>>>>>> support_for_custom_statement_designs
"shasum": ""
},
"require": {
@ -574,9 +587,15 @@
"support": {
"forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
"issues": "https://github.com/aws/aws-sdk-php/issues",
<<<<<<< HEAD
"source": "https://github.com/aws/aws-sdk-php/tree/3.285.3"
},
"time": "2023-11-09T19:07:19+00:00"
=======
"source": "https://github.com/aws/aws-sdk-php/tree/3.284.0"
},
"time": "2023-11-03T18:13:48+00:00"
>>>>>>> support_for_custom_statement_designs
},
{
"name": "bacon/bacon-qr-code",
@ -686,6 +705,7 @@
},
{
"name": "braintree/braintree_php",
<<<<<<< HEAD
"version": "6.15.0",
"source": {
"type": "git",
@ -696,6 +716,18 @@
"type": "zip",
"url": "https://api.github.com/repos/braintree/braintree_php/zipball/16efb08e19cb6c579deba11e119ef6409d28eae3",
"reference": "16efb08e19cb6c579deba11e119ef6409d28eae3",
=======
"version": "6.14.0",
"source": {
"type": "git",
"url": "https://github.com/braintree/braintree_php.git",
"reference": "084ed5bb728bc32ad444c6d043c87b409cca72d7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/braintree/braintree_php/zipball/084ed5bb728bc32ad444c6d043c87b409cca72d7",
"reference": "084ed5bb728bc32ad444c6d043c87b409cca72d7",
>>>>>>> support_for_custom_statement_designs
"shasum": ""
},
"require": {
@ -729,9 +761,15 @@
"description": "Braintree PHP Client Library",
"support": {
"issues": "https://github.com/braintree/braintree_php/issues",
<<<<<<< HEAD
"source": "https://github.com/braintree/braintree_php/tree/6.15.0"
},
"time": "2023-11-08T00:15:11+00:00"
=======
"source": "https://github.com/braintree/braintree_php/tree/6.14.0"
},
"time": "2023-10-18T22:00:15+00:00"
>>>>>>> support_for_custom_statement_designs
},
{
"name": "brick/math",
@ -4230,6 +4268,7 @@
},
{
"name": "laravel/framework",
<<<<<<< HEAD
"version": "v10.31.0",
"source": {
"type": "git",
@ -4240,6 +4279,18 @@
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/507ce9b28bce4b5e4140c28943092ca38e9a52e4",
"reference": "507ce9b28bce4b5e4140c28943092ca38e9a52e4",
=======
"version": "v10.30.1",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "7a2da50258c4d0f693b738d3f3c69b2693aea6c1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/7a2da50258c4d0f693b738d3f3c69b2693aea6c1",
"reference": "7a2da50258c4d0f693b738d3f3c69b2693aea6c1",
>>>>>>> support_for_custom_statement_designs
"shasum": ""
},
"require": {
@ -4428,7 +4479,11 @@
"issues": "https://github.com/laravel/framework/issues",
"source": "https://github.com/laravel/framework"
},
<<<<<<< HEAD
"time": "2023-11-07T13:48:30+00:00"
=======
"time": "2023-11-01T13:52:17+00:00"
>>>>>>> support_for_custom_statement_designs
},
{
"name": "laravel/prompts",
@ -5225,6 +5280,7 @@
},
{
"name": "league/flysystem",
<<<<<<< HEAD
"version": "3.19.0",
"source": {
"type": "git",
@ -5235,6 +5291,18 @@
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/1b2aa10f2326e0351399b8ce68e287d8e9209a83",
"reference": "1b2aa10f2326e0351399b8ce68e287d8e9209a83",
=======
"version": "3.18.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem.git",
"reference": "015633a05aee22490495159237a5944091d8281e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/015633a05aee22490495159237a5944091d8281e",
"reference": "015633a05aee22490495159237a5944091d8281e",
>>>>>>> support_for_custom_statement_designs
"shasum": ""
},
"require": {
@ -5299,7 +5367,11 @@
],
"support": {
"issues": "https://github.com/thephpleague/flysystem/issues",
<<<<<<< HEAD
"source": "https://github.com/thephpleague/flysystem/tree/3.19.0"
=======
"source": "https://github.com/thephpleague/flysystem/tree/3.18.0"
>>>>>>> support_for_custom_statement_designs
},
"funding": [
{
@ -5311,7 +5383,11 @@
"type": "github"
}
],
<<<<<<< HEAD
"time": "2023-11-07T09:04:28+00:00"
=======
"time": "2023-10-20T17:59:40+00:00"
>>>>>>> support_for_custom_statement_designs
},
{
"name": "league/flysystem-aws-s3-v3",
@ -5381,6 +5457,7 @@
},
{
"name": "league/flysystem-local",
<<<<<<< HEAD
"version": "3.19.0",
"source": {
"type": "git",
@ -5391,6 +5468,18 @@
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/8d868217f9eeb4e9a7320db5ccad825e9a7a4076",
"reference": "8d868217f9eeb4e9a7320db5ccad825e9a7a4076",
=======
"version": "3.18.0",
"source": {
"type": "git",
"url": "https://github.com/thephpleague/flysystem-local.git",
"reference": "e7381ef7643f658b87efb7dbe98fe538fb1bbf32"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/e7381ef7643f658b87efb7dbe98fe538fb1bbf32",
"reference": "e7381ef7643f658b87efb7dbe98fe538fb1bbf32",
>>>>>>> support_for_custom_statement_designs
"shasum": ""
},
"require": {
@ -5425,7 +5514,11 @@
],
"support": {
"issues": "https://github.com/thephpleague/flysystem-local/issues",
<<<<<<< HEAD
"source": "https://github.com/thephpleague/flysystem-local/tree/3.19.0"
=======
"source": "https://github.com/thephpleague/flysystem-local/tree/3.18.0"
>>>>>>> support_for_custom_statement_designs
},
"funding": [
{
@ -5437,7 +5530,11 @@
"type": "github"
}
],
<<<<<<< HEAD
"time": "2023-11-06T20:35:28+00:00"
=======
"time": "2023-10-19T20:07:13+00:00"
>>>>>>> support_for_custom_statement_designs
},
{
"name": "league/fractal",
@ -5831,6 +5928,7 @@
},
{
"name": "mollie/mollie-api-php",
<<<<<<< HEAD
"version": "v2.63.0",
"source": {
"type": "git",
@ -5841,6 +5939,18 @@
"type": "zip",
"url": "https://api.github.com/repos/mollie/mollie-api-php/zipball/642f1b87624bd1535cd198134113e14bc01ba245",
"reference": "642f1b87624bd1535cd198134113e14bc01ba245",
=======
"version": "v2.62.0",
"source": {
"type": "git",
"url": "https://github.com/mollie/mollie-api-php.git",
"reference": "feb6d52859ed1ea7a65b25bb6cbfaadb04b33827"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/mollie/mollie-api-php/zipball/feb6d52859ed1ea7a65b25bb6cbfaadb04b33827",
"reference": "feb6d52859ed1ea7a65b25bb6cbfaadb04b33827",
>>>>>>> support_for_custom_statement_designs
"shasum": ""
},
"require": {
@ -5917,9 +6027,15 @@
],
"support": {
"issues": "https://github.com/mollie/mollie-api-php/issues",
<<<<<<< HEAD
"source": "https://github.com/mollie/mollie-api-php/tree/v2.63.0"
},
"time": "2023-11-06T09:20:50+00:00"
=======
"source": "https://github.com/mollie/mollie-api-php/tree/v2.62.0"
},
"time": "2023-10-23T11:22:58+00:00"
>>>>>>> support_for_custom_statement_designs
},
{
"name": "moneyphp/money",
@ -7613,6 +7729,7 @@
},
{
"name": "php-http/promise",
<<<<<<< HEAD
"version": "1.2.1",
"source": {
"type": "git",
@ -7623,6 +7740,18 @@
"type": "zip",
"url": "https://api.github.com/repos/php-http/promise/zipball/44a67cb59f708f826f3bec35f22030b3edb90119",
"reference": "44a67cb59f708f826f3bec35f22030b3edb90119",
=======
"version": "1.2.0",
"source": {
"type": "git",
"url": "https://github.com/php-http/promise.git",
"reference": "ef4905bfb492ff389eb7f12e26925a0f20073050"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-http/promise/zipball/ef4905bfb492ff389eb7f12e26925a0f20073050",
"reference": "ef4905bfb492ff389eb7f12e26925a0f20073050",
>>>>>>> support_for_custom_statement_designs
"shasum": ""
},
"require": {
@ -7659,9 +7788,15 @@
],
"support": {
"issues": "https://github.com/php-http/promise/issues",
<<<<<<< HEAD
"source": "https://github.com/php-http/promise/tree/1.2.1"
},
"time": "2023-11-08T12:57:08+00:00"
=======
"source": "https://github.com/php-http/promise/tree/1.2.0"
},
"time": "2023-10-24T09:20:26+00:00"
>>>>>>> support_for_custom_statement_designs
},
{
"name": "php-jsonpointer/php-jsonpointer",
@ -14825,6 +14960,7 @@
},
{
"name": "friendsofphp/php-cs-fixer",
<<<<<<< HEAD
"version": "v3.38.0",
"source": {
"type": "git",
@ -14835,6 +14971,18 @@
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/7e6070026e76aa09d77a47519625c86593fb8e31",
"reference": "7e6070026e76aa09d77a47519625c86593fb8e31",
=======
"version": "v3.37.1",
"source": {
"type": "git",
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
"reference": "c3fe76976081ab871aa654e872da588077e19679"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/c3fe76976081ab871aa654e872da588077e19679",
"reference": "c3fe76976081ab871aa654e872da588077e19679",
>>>>>>> support_for_custom_statement_designs
"shasum": ""
},
"require": {
@ -14906,7 +15054,11 @@
],
"support": {
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
<<<<<<< HEAD
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.38.0"
=======
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.37.1"
>>>>>>> support_for_custom_statement_designs
},
"funding": [
{
@ -14914,7 +15066,11 @@
"type": "github"
}
],
<<<<<<< HEAD
"time": "2023-11-07T08:44:54+00:00"
=======
"time": "2023-10-29T20:51:23+00:00"
>>>>>>> support_for_custom_statement_designs
},
{
"name": "hamcrest/hamcrest-php",

View File

@ -47,7 +47,7 @@ class FacturaeTest extends TestCase
$this->assertNotNull($f->run());
nlog($f->run());
// nlog($f->run());
// $this->assertTrue($this->validateInvoiceXML($path));
}

View File

@ -104,6 +104,90 @@ class TaskApiTest extends TestCase
}
}
public function testEmptyTimeLogArray()
{
$data = [
'client_id' => $this->client->id,
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'description' => 'Test Task',
'time_log' => null,
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson("/api/v1/tasks", $data);
$response->assertStatus(200);
$data = [
'client_id' => $this->client->id,
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'description' => 'Test Task',
'time_log' => '',
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson("/api/v1/tasks", $data);
$response->assertStatus(200);
$data = [
'client_id' => $this->client->id,
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'description' => 'Test Task',
'time_log' => '[]',
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson("/api/v1/tasks", $data);
$response->assertStatus(200);
$data = [
'client_id' => $this->client->id,
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'description' => 'Test Task',
'time_log' => '{}',
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson("/api/v1/tasks", $data);
$response->assertStatus(200);
}
public function testFaultyTimeLogArray()
{
$data = [
'client_id' => $this->client->id,
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'description' => 'Test Task',
'time_log' => 'ABBA is the best band in the world',
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->postJson("/api/v1/tasks", $data);
$response->assertStatus(422);
}
public function testTaskClientRateSet()
{
$settings = ClientSettings::defaults();
@ -282,6 +366,45 @@ class TaskApiTest extends TestCase
$response->assertStatus(200);
$task->time_log = 'A very strange place';
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->putJson("/api/v1/tasks/{$task->hashed_id}?stop=true", $task->toArray());
$response->assertStatus(422);
$task->time_log = null;
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->putJson("/api/v1/tasks/{$task->hashed_id}?stop=true", $task->toArray());
$response->assertStatus(200);
$task->time_log = '';
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->putJson("/api/v1/tasks/{$task->hashed_id}?stop=true", $task->toArray());
$response->assertStatus(200);
$task->time_log = '{}';
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token,
])->putJson("/api/v1/tasks/{$task->hashed_id}?stop=true", $task->toArray());
$response->assertStatus(200);
}
public function testStoppingTaskWithDescription()

View File

@ -18,6 +18,7 @@ use App\Models\Credit;
use App\Models\Design;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\Project;
use App\Utils\HtmlEngine;
use Tests\MockAccountData;
use App\Utils\Traits\MakesDates;
@ -165,6 +166,8 @@ class TemplateTest extends TestCase
</ninja>
';
private string $stack = '<html><div id="company-details" labels="true"></div></html>';
protected function setUp() :void
{
parent::setUp();
@ -177,6 +180,136 @@ class TemplateTest extends TestCase
}
public function testPurchaseOrderDataParse()
{
$data = [];
$p = \App\Models\PurchaseOrder::factory()->create([
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'vendor_id' => $this->vendor->id,
]);
$data['purchase_orders'][] = $p;
$ts = new TemplateService();
$ts->processData($data);
$this->assertNotNull($ts);
$this->assertIsArray($ts->getData());
}
public function testTaskDataParse()
{
$data = [];
$p = \App\Models\Task::factory()->create([
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'client_id' => $this->client->id,
]);
$data['tasks'][] = $p;
$ts = new TemplateService();
$ts->processData($data);
$this->assertNotNull($ts);
$this->assertIsArray($ts->getData());
}
public function testQuoteDataParse()
{
$data = [];
$p = \App\Models\Quote::factory()->create([
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'client_id' => $this->client->id,
]);
$data['quotes'][] = $p;
$ts = new TemplateService();
$ts->processData($data);
$this->assertNotNull($ts);
$this->assertIsArray($ts->getData());
}
public function testProjectDataParse()
{
$data = [];
$p = Project::factory()->create([
'user_id' => $this->user->id,
'company_id' => $this->company->id,
'client_id' => $this->client->id,
]);
$data['projects'][] = $p;
$ts = new TemplateService();
$ts->processData($data);
$this->assertNotNull($ts);
$this->assertIsArray($ts->getData());
}
public function testNegativeDivAttribute()
{
$dom = new \DOMDocument();
@$dom->loadHTML(mb_convert_encoding($this->stack, 'HTML-ENTITIES', 'UTF-8'));
$node = $dom->getElementById('company-details');
$x = $node->getAttribute('nonexistentattribute');
$this->assertEquals('', $x);
}
public function testStackResolutionWithLabels()
{
$dom = new \DOMDocument();
@$dom->loadHTML(mb_convert_encoding($this->stack, 'HTML-ENTITIES', 'UTF-8'));
$node = $dom->getElementById('company-details');
$x = $node->getAttribute('labels');
$this->assertEquals('true', $x);
}
public function testStackResolution()
{
$partials['design']['includes'] = '';
$partials['design']['header'] = '';
$partials['design']['body'] = $this->stack;
$partials['design']['footer'] = '';
$tm = new TemplateMock($this->company);
$tm->init();
$variables = $tm->variables[0];
$ts = new TemplateService();
$x = $ts->setTemplate($partials)
->setCompany($this->company)
->overrideVariables($variables)
->parseGlobalStacks()
->parseVariables()
->getHtml();
$this->assertIsString($x);
}
public function testDataMaps()
{
$start = microtime(true);
@ -319,19 +452,8 @@ class TemplateTest extends TestCase
];
});
$queries = \DB::getQueryLog();
$count = count($queries);
nlog("query count = {$count}");
$x = $invoices->toArray();
// nlog(json_encode($x));
// nlog(json_encode(htmlspecialchars(json_encode($x), ENT_QUOTES, 'UTF-8')));
// nlog($invoices->toJson());
$this->assertIsArray($invoices->toArray());
nlog("end invoices = " . microtime(true) - $start);
}
private function transformPayment(Payment $payment): array
@ -466,8 +588,6 @@ class TemplateTest extends TestCase
$data['invoices'] = $invoices;
$ts = $replicated_design->service()->build($data);
// nlog("results = ");
// nlog($ts->getHtml());
$this->assertNotNull($ts->getHtml());
}