mirror of
https://github.com/Karaka-Management/oms-Billing.git
synced 2026-02-10 13:28:42 +00:00
crash backup
This commit is contained in:
parent
0647f6da2b
commit
b36936274d
16
Admin/Hooks/Cli.php
Normal file
16
Admin/Hooks/Cli.php
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
/**
|
||||
* Jingga
|
||||
*
|
||||
* PHP Version 8.2
|
||||
*
|
||||
* @package Modules\Billing
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 2.2
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
];
|
||||
29
Admin/Hooks/Web/Api.php
Normal file
29
Admin/Hooks/Web/Api.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
/**
|
||||
* Jingga
|
||||
*
|
||||
* PHP Version 8.2
|
||||
*
|
||||
* @package Modules\Admin
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 2.2
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
return [
|
||||
"POST:Billing:bill-create" => [
|
||||
'callback' => ['\Modules\Workflow\Controller\ApiController:hookWorkflowChangeState'],
|
||||
],
|
||||
"POST:Billing:bill-update" => [
|
||||
'callback' => ['\Modules\Workflow\Controller\ApiController:hookWorkflowChangeState'],
|
||||
],
|
||||
|
||||
"POST:Billing:bill_element-create" => [
|
||||
'callback' => ['\Modules\Workflow\Controller\ApiController:hookWorkflowChangeState'],
|
||||
],
|
||||
"POST:Billing:bill_element-update" => [
|
||||
'callback' => ['\Modules\Workflow\Controller\ApiController:hookWorkflowChangeState'],
|
||||
],
|
||||
];
|
||||
|
|
@ -1,32 +1,38 @@
|
|||
{
|
||||
"templates": [
|
||||
{
|
||||
"name": "Billing workflow",
|
||||
"path": "/Modules/Billing/Admin/Install/Workflow/bill"
|
||||
}
|
||||
],
|
||||
"triggers": [
|
||||
"PRE:Module:Billing:bill-create",
|
||||
"POST:Module:Billing:bill-create",
|
||||
"PRE:Module:Billing:bill-update",
|
||||
"POST:Module:Billing:bill-update",
|
||||
"PRE:Module:Billing:bill-delete",
|
||||
"POST:Module:Billing:bill-delete",
|
||||
"PRE:Billing:bill-create",
|
||||
"POST:Billing:bill-create",
|
||||
"PRE:Billing:bill-update",
|
||||
"POST:Billing:bill-update",
|
||||
"PRE:Billing:bill-delete",
|
||||
"POST:Billing:bill-delete",
|
||||
|
||||
"PRE:Module:Billing:bill_element-create",
|
||||
"POST:Module:Billing:bill_element-create",
|
||||
"PRE:Module:Billing:bill_element-update",
|
||||
"POST:Module:Billing:bill_element-update",
|
||||
"PRE:Module:Billing:bill_element-delete",
|
||||
"POST:Module:Billing:bill_element-delete",
|
||||
"PRE:Billing:bill_element-create",
|
||||
"POST:Billing:bill_element-create",
|
||||
"PRE:Billing:bill_element-update",
|
||||
"POST:Billing:bill_element-update",
|
||||
"PRE:Billing:bill_element-delete",
|
||||
"POST:Billing:bill_element-delete",
|
||||
|
||||
"PRE:Module:Billing:bill_media-create",
|
||||
"POST:Module:Billing:bill_media-create",
|
||||
"PRE:Module:Billing:bill_media-update",
|
||||
"POST:Module:Billing:bill_media-update",
|
||||
"PRE:Module:Billing:bill_media-delete",
|
||||
"POST:Module:Billing:bill_media-delete",
|
||||
"PRE:Billing:bill_media-create",
|
||||
"POST:Billing:bill_media-create",
|
||||
"PRE:Billing:bill_media-update",
|
||||
"POST:Billing:bill_media-update",
|
||||
"PRE:Billing:bill_media-delete",
|
||||
"POST:Billing:bill_media-delete",
|
||||
|
||||
"PRE:Module:Billing:bill_note-create",
|
||||
"POST:Module:Billing:bill_note-create",
|
||||
"PRE:Module:Billing:bill_note-update",
|
||||
"POST:Module:Billing:bill_note-update",
|
||||
"PRE:Module:Billing:bill_note-delete",
|
||||
"POST:Module:Billing:bill_note-delete"
|
||||
"PRE:Billing:bill_note-create",
|
||||
"POST:Billing:bill_note-create",
|
||||
"PRE:Billing:bill_note-update",
|
||||
"POST:Billing:bill_note-update",
|
||||
"PRE:Billing:bill_note-delete",
|
||||
"POST:Billing:bill_note-delete"
|
||||
],
|
||||
"actions": {
|
||||
"1005100001": {
|
||||
|
|
|
|||
319
Admin/Install/Workflow/bill/WorkflowController.php
Normal file
319
Admin/Install/Workflow/bill/WorkflowController.php
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
<?php
|
||||
/**
|
||||
* Jingga
|
||||
*
|
||||
* PHP Version 8.2
|
||||
*
|
||||
* @package Modules\Workflow\Controller
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\Workflow\Controller;
|
||||
|
||||
use Modules\Admin\Models\GroupMapper;
|
||||
use Modules\Admin\Models\NullAccount;
|
||||
use Modules\Admin\Models\NullGroup;
|
||||
use Modules\Auditor\Models\Audit;
|
||||
use Modules\Auditor\Models\AuditMapper;
|
||||
use Modules\Billing\Models\Bill;
|
||||
use Modules\Billing\Models\BillElementMapper;
|
||||
use Modules\Billing\Models\BillMapper;
|
||||
use Modules\Billing\Models\BillStatus;
|
||||
use Modules\Billing\Models\PermissionCategory;
|
||||
use Modules\Billing\Models\WorkflowStepStatusEnum;
|
||||
use Modules\Billing\Models\WorkflowType;
|
||||
use Modules\Tasks\Models\Task;
|
||||
use Modules\Tasks\Models\TaskElement;
|
||||
use Modules\Tasks\Models\TaskMapper;
|
||||
use Modules\Tasks\Models\TaskPriority;
|
||||
use Modules\Tasks\Models\TaskStatus;
|
||||
use Modules\Tasks\Models\TaskType;
|
||||
use Modules\Workflow\Models\NullWorkflowTemplate;
|
||||
use Modules\Workflow\Models\WorkflowControllerInterface;
|
||||
use Modules\Workflow\Models\WorkflowInstanceAbstract;
|
||||
use Modules\Workflow\Models\WorkflowTemplate;
|
||||
use phpOMS\Message\RequestAbstract;
|
||||
use phpOMS\Message\ResponseAbstract;
|
||||
use phpOMS\Views\View;
|
||||
use Modules\Workflow\Models\WorkflowInstanceAbstractMapper;
|
||||
use Modules\Workflow\Models\WorkflowStep;
|
||||
use phpOMS\Utils\StringUtils;
|
||||
|
||||
/**
|
||||
* OMS export class
|
||||
*
|
||||
* @package Modules\Workflow\Controller
|
||||
* @license OMS License 2.0
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
final class WorkflowController implements WorkflowControllerInterface
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createInstanceFromRequest(RequestAbstract $request, WorkflowTemplate $template) : WorkflowInstanceAbstract
|
||||
{
|
||||
$instance = new WorkflowInstanceAbstract();
|
||||
$instance->createdBy = new NullAccount($request->header->account);
|
||||
$instance->title = $template->name . ': ' . ($request->getDataString('title') ?? '');
|
||||
$instance->template = new NullWorkflowTemplate($request->getData('template') ?? 0);
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getInstanceListFromRequest(RequestAbstract $request) : array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createTemplateViewFromRequest(View $view, RequestAbstract $request, ResponseAbstract $response) : void
|
||||
{
|
||||
$includes = 'Modules/Media/Files/';
|
||||
$tpl = $view->data['template']->source->findFile('template-profile.tpl.php');
|
||||
|
||||
$start = \stripos($tpl->getPath(), $includes);
|
||||
|
||||
$view->setTemplate('/' . \substr($tpl->getPath(), $start, -8));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createInstanceViewFromRequest(View $view, RequestAbstract $request, ResponseAbstract $response) : void
|
||||
{
|
||||
$includes = 'Modules/Media/Files/';
|
||||
$tpl = $view->data['template']->source->findFile('instance-profile.tpl.php');
|
||||
|
||||
$start = \stripos($tpl->getPath(), $includes);
|
||||
|
||||
$view->setTemplate('/' . \substr($tpl->getPath(), $start, -8));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apiChangeState(RequestAbstract $request, ResponseAbstract $response, $data = []) : void
|
||||
{
|
||||
// @bug what if we create two tasks for the same element (due to edits) before it got closed
|
||||
// In that case the automatic task closing will only effect the most recent task
|
||||
// Maybe check task existence first and close it before creating a new task
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function hookChangeState(
|
||||
WorkflowTemplate $template,
|
||||
int $account,
|
||||
mixed $old,
|
||||
mixed $new,
|
||||
?int $type = null,
|
||||
string $trigger = '',
|
||||
?string $module = null,
|
||||
?string $ref = null,
|
||||
?string $content = null,
|
||||
?string $ip = null
|
||||
) : mixed
|
||||
{
|
||||
$old = null;
|
||||
|
||||
/** @var Bill $bill */
|
||||
$bill = null;
|
||||
if ($type === StringUtils::intHash(BillMapper::class)) {
|
||||
$bill = $new;
|
||||
} else if ($type === StringUtils::intHash(BillElementMapper::class)) {
|
||||
/** @var BillElement $element */
|
||||
$element = $new;
|
||||
$bill = BillMapper::get()
|
||||
->where('id', $element->bill->id)
|
||||
->executeGet();
|
||||
}
|
||||
|
||||
if ($bill->status !== BillStatus::DONE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$instance = WorkflowInstanceAbstractMapper::get()
|
||||
->with('steps')
|
||||
->with('template')
|
||||
->where('template/module', 'Billing')
|
||||
->where('template/type', $bill->client != null ? WorkflowType::SALES_BILL : WorkflowType::PURCHASE_BILL)
|
||||
->where('ref', $bill->id)
|
||||
->executeGet();
|
||||
|
||||
$instance->template = $template;
|
||||
$old = clone $instance;
|
||||
|
||||
if ($type === StringUtils::intHash(BillMapper::class)) {
|
||||
$actionType = $this->hookChangeBillState($instance, $bill);
|
||||
} else if ($type === StringUtils::intHash(BillElementMapper::class)) {
|
||||
$actionType = $this->hookChangeBillElementState($instance, $bill);
|
||||
}
|
||||
|
||||
if ($actionType < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// $actionType === 0 -> create/update instance but don't create a task
|
||||
if ($instance->id === 0) {
|
||||
// Create new instance if none exist for this element (ref)
|
||||
$instance->template = $template;
|
||||
$instance->ref = $element->bill->id;
|
||||
|
||||
WorkflowInstanceAbstractMapper::create()
|
||||
->execute($instance);
|
||||
|
||||
$oldSerialized = '';
|
||||
} else {
|
||||
WorkflowInstanceAbstractMapper::update()
|
||||
->execute($instance);
|
||||
|
||||
$oldSerialized = $old->jsonSerialize();
|
||||
}
|
||||
|
||||
$audit = new Audit(
|
||||
new NullAccount($account),
|
||||
$oldSerialized,
|
||||
$instance->jsonSerialize(),
|
||||
StringUtils::intHash(WorkflowInstanceAbstract::class),
|
||||
$trigger,
|
||||
$module,
|
||||
$ref,
|
||||
$content,
|
||||
(int) \ip2long($ip ?? '127.0.0.1')
|
||||
);
|
||||
AuditMapper::create()->execute($audit);
|
||||
|
||||
if ($actionType === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create task for users
|
||||
// @bug We are currently searching the groups by static category. This is wrong
|
||||
// Workflows could be different per company (multiple levels) -> we need to be able to define the group differently
|
||||
// Maybe we define the group that does a specific step as a setting either in the settings table or settings.json file of the workflow
|
||||
$groups = GroupMapper::findReadPermission(
|
||||
$this->app->unitId,
|
||||
'Billing',
|
||||
PermissionCategory::SALES_INVOICE,
|
||||
$bill->id
|
||||
);
|
||||
|
||||
$task = new Task();
|
||||
$task->title = '';
|
||||
$task->description = '';
|
||||
$task->descriptionRaw = '';
|
||||
$task->createdBy = new NullAccount($account);
|
||||
$task->status = TaskStatus::OPEN;
|
||||
$task->type = TaskType::SINGLE;
|
||||
$task->redirect = '{/base}/workflow/instance/view?{?}&id=' . $instance->id;
|
||||
$task->unit = $bill->unit;
|
||||
$task->priority = TaskPriority::HIGH;
|
||||
|
||||
$element = new TaskElement();
|
||||
$element->createdBy = $task->createdBy;
|
||||
$element->due = $task->due;
|
||||
$element->priority = $task->priority;
|
||||
$element->status = TaskStatus::OPEN;
|
||||
|
||||
foreach ($groups as $group) {
|
||||
$element->addTo(new NullGroup($group));
|
||||
}
|
||||
|
||||
$task->addElement($element);
|
||||
|
||||
TaskMapper::create()->execute($task);
|
||||
|
||||
// TaskStep data:
|
||||
/*
|
||||
[
|
||||
{
|
||||
'task_id' => 0,
|
||||
'element_id' => 0,
|
||||
'level' => 1, // sometimes multiple levels exist per approval (e.g. price until x approved by cso, then cfo, then ceo)
|
||||
'approved' => true,
|
||||
|
||||
'max_price' => ,
|
||||
'max_price' => ,
|
||||
}
|
||||
];
|
||||
*/
|
||||
// @todo steps should have notes for communication (maybe use task elements for that). This way people can communicate
|
||||
// Maybe create notifications whenever a new note is created?
|
||||
|
||||
return $instance;
|
||||
}
|
||||
|
||||
private function hookChangeBillState(WorkflowInstanceAbstract $instance, Bill $bill) : bool {
|
||||
// if all states approved -> set to approved
|
||||
// careful, if bill create -> check if elements are approved
|
||||
|
||||
$instance->data_int = WorkflowStepStatusEnum::BILL_APPROVAL;
|
||||
|
||||
$foundStep = null;
|
||||
|
||||
// Find matching step (if exists)
|
||||
foreach ($instance->steps as $step) {
|
||||
|
||||
}
|
||||
|
||||
// @bug It could be multiple steps that we need to handle
|
||||
// Create new step if not existing
|
||||
if ($foundStep !== null) {
|
||||
$foundStep = new WorkflowStep();
|
||||
$instance->steps[] = $foundStep;
|
||||
}
|
||||
|
||||
// new bill
|
||||
// check credit limit
|
||||
// check payment term
|
||||
// check payment approval
|
||||
// check total price
|
||||
// check total gross profit
|
||||
// check correctness
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function hookChangeBillElementState(WorkflowInstanceAbstract $instance, Bill $bill) : bool {
|
||||
// if element within range -> set to approved (unless element exists and is not approved)
|
||||
|
||||
// new element
|
||||
// quantity changed
|
||||
// price changed
|
||||
|
||||
// check price
|
||||
// check quantity
|
||||
// check gross profit
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function apiHandleState(RequestAbstract $request, ResponseAbstract $response, $data = []) : void
|
||||
{
|
||||
if (($data['trigger'] ?? null) === 'PRE:Billing-print') {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function createInstanceDbModel(WorkflowInstanceAbstract $instance) : void
|
||||
{
|
||||
}
|
||||
}
|
||||
0
Admin/Install/Workflow/bill/WorkflowState.php
Normal file
0
Admin/Install/Workflow/bill/WorkflowState.php
Normal file
49
Admin/Install/Workflow/bill/instance-list.tpl.php
Normal file
49
Admin/Install/Workflow/bill/instance-list.tpl.php
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
/**
|
||||
* Jingga
|
||||
*
|
||||
* PHP Version 8.2
|
||||
*
|
||||
* @package Modules\Workflow
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
use phpOMS\Uri\UriFactory;
|
||||
|
||||
$instances = $this->data['instances'];
|
||||
|
||||
$previous = empty($instances) ? 'workflows/instance/list' : '{/base}/workflows/instance/list?{?}&offset=' . \reset($instances)->id . '&ptype=p';
|
||||
$next = empty($instances) ? 'workflows/instance/list' : '{/base}/workflows/instance/list?{?}&offset=' . \end($instances)->id . '&ptype=n';
|
||||
|
||||
echo $this->data['nav']->render();
|
||||
?>
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<section class="portlet">
|
||||
<div class="portlet-head"><?= $this->getHtml('Instances'); ?><i class="g-icon download btn end-xs">download</i></div>
|
||||
<div class="slider">
|
||||
<table id="helperList" class="default sticky">
|
||||
<thead>
|
||||
<tr>
|
||||
<td><label class="checkbox" for="helperList-0">
|
||||
<input type="checkbox" id="helperList-0" name="helperselect">
|
||||
<span class="checkmark"></span>
|
||||
</label>
|
||||
<td>
|
||||
</tr>
|
||||
<tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--
|
||||
<div class="portlet-foot">
|
||||
<a tabindex="0" class="button" href="<?= UriFactory::build($previous); ?>"><?= $this->getHtml('Previous', '0', '0'); ?></a>
|
||||
<a tabindex="0" class="button" href="<?= UriFactory::build($next); ?>"><?= $this->getHtml('Next', '0', '0'); ?></a>
|
||||
</div>
|
||||
-->
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
54
Admin/Install/Workflow/bill/instance-profile.tpl.php
Normal file
54
Admin/Install/Workflow/bill/instance-profile.tpl.php
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
/**
|
||||
* Jingga
|
||||
*
|
||||
* PHP Version 8.2
|
||||
*
|
||||
* @package Modules\Workflow
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 2.0
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
echo $this->data['nav']->render();
|
||||
?>
|
||||
<div class="tabview tab-2">
|
||||
<div class="box">
|
||||
<ul class="tab-links">
|
||||
<li><label for="c-tab-1"><?= $this->getHtml('Overview'); ?></label>
|
||||
<li><label for="c-tab-1"><?= $this->getHtml('Bill'); ?></label>
|
||||
<li><label for="c-tab-1"><?= $this->getHtml('CreditLimit'); ?></label>
|
||||
<li><label for="c-tab-1"><?= $this->getHtml('PaymentTerm'); ?></label>
|
||||
<li><label for="c-tab-2"><?= $this->getHtml('Elements'); ?></label>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tab-content">
|
||||
<input type="radio" id="c-tab-1" name="tabular-2"<?= empty($this->request->uri->fragment) || $this->request->uri->fragment === 'c-tab-1' ? ' checked' : ''; ?>>
|
||||
<div class="tab">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
// create colored list (grey, red, green) of the different steps
|
||||
// grey = no action needed
|
||||
// yellow = action needed
|
||||
// green = approved
|
||||
// red = declined
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="radio" id="c-tab-1" name="tabular-2"<?= empty($this->request->uri->fragment) || $this->request->uri->fragment === 'c-tab-1' ? ' checked' : ''; ?>>
|
||||
<div class="tab">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<section class="portlet">
|
||||
<div class="portlet-head">Profile</div>
|
||||
<div class="portlet-body"></div>
|
||||
<div class="portlet-foot"></div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
18
Admin/Install/Workflow/bill/lang.php
Normal file
18
Admin/Install/Workflow/bill/lang.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
return [
|
||||
'en' => [
|
||||
'Overview' => 'Overview',
|
||||
'Bill' => 'Bill',
|
||||
'CreditLimit' => 'Credit Limit',
|
||||
'PaymentTerm' => 'Payment Term',
|
||||
'Elements' => 'Elements',
|
||||
],
|
||||
'de' => [
|
||||
'Overview' => 'Übersicht',
|
||||
'Bill' => 'Beleg',
|
||||
'CreditLimit' => 'Kreditlimit',
|
||||
'PaymentTerm' => 'Zahlungsbedingung',
|
||||
'Elements' => 'Belegzeilen',
|
||||
],
|
||||
];
|
||||
0
Admin/Install/Workflow/bill/settings.json
Normal file
0
Admin/Install/Workflow/bill/settings.json
Normal file
0
Admin/Install/Workflow/bill/settings.tpl.php
Normal file
0
Admin/Install/Workflow/bill/settings.tpl.php
Normal file
131
Admin/Install/Workflow/bill/template-profile.tpl.php
Normal file
131
Admin/Install/Workflow/bill/template-profile.tpl.php
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
use phpOMS\Uri\UriFactory;
|
||||
|
||||
/** @var \Modules\Workflow\Models\WorkflowTemplate $template */
|
||||
$template = $this->data['template'];
|
||||
$media = $template->source->getSources();
|
||||
|
||||
$lang = include $template->source->findFile('lang.php')->getAbsolutePath();
|
||||
|
||||
echo $this->data['nav']->render(); ?>
|
||||
|
||||
<div id="iSettings" class="tabview tab-2 url-rewrite">
|
||||
<div class="box">
|
||||
<ul class="tab-links">
|
||||
<li><label for="c-tab-1"><?= $this->getHtml('General'); ?></label>
|
||||
<li><label for="c-tab-2"><?= $this->getHtml('Workflow'); ?></label>
|
||||
<li><label for="c-tab-3"><?= $this->getHtml('Settings'); ?></label>
|
||||
<li><label for="c-tab-4"><?= $this->getHtml('Files'); ?></label>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="tab-content">
|
||||
<input type="radio" id="c-tab-1" name="tabular-2"<?= empty($this->request->uri->fragment) || $this->request->uri->fragment === 'c-tab-1' ? ' checked' : ''; ?>>
|
||||
<div class="tab">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<section class="portlet">
|
||||
<form id="iGeneralSettings" action="<?= UriFactory::build('{/api}admin/settings/general?csrf={$CSRF}'); ?>" method="post">
|
||||
<div class="portlet-head"><?= $this->getHtml('General'); ?></div>
|
||||
<div class="portlet-body">
|
||||
<div class="form-group">
|
||||
<label for="iOname"><?= $this->getHtml('Name'); ?></label>
|
||||
<input type="text" value="<?= $template->name; ?>">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="iOname"><?= $this->getHtml('Description'); ?></label>
|
||||
<textarea></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="radio" id="c-tab-2" name="tabular-2"<?= empty($this->request->uri->fragment) || $this->request->uri->fragment === 'c-tab-1' ? ' checked' : ''; ?>>
|
||||
<div class="tab">
|
||||
<div class="row">
|
||||
<div class="col-xs-12">
|
||||
<section class="portlet">
|
||||
<div class="portlet-body">
|
||||
<div class="mermaid">
|
||||
flowchart TB;
|
||||
CREATE_BILL[Create bill]-->LOCKED{Is locked?};
|
||||
LOCKED-->|TRUE|CREATE_APPROVAL_TASK[Accounting approval task];
|
||||
LOCKED-->|FALSE|PRINTABLE;
|
||||
CREATE_APPROVAL_TASK-->ACCOUNTING_APPROVAL{Is ok?};
|
||||
ACCOUNTING_APPROVAL-->|FALSE|ACCOUNTING_NOT_APPROVED[Inform OP];
|
||||
ACCOUNTING_APPROVAL-->|TRUE|PRINTABLE;
|
||||
CREATE_BILL-->CREATE_CHECK_TASK[Invoice validation task];
|
||||
CREATE_CHECK_TASK-->BILL_CHECK{Is correct?};
|
||||
BILL_CHECK-->|TRUE|CHECK_PRICES{High discounts?};
|
||||
CHECK_PRICES-->|FALSE|PRINTABLE;
|
||||
BILL_CHECK-->|FALSE|INFO_WRITER[Inform OP];
|
||||
CHECK_PRICES-->|TRUE|CREATE_SALES_APPROVAL_TASK[Sales approval task];
|
||||
CREATE_SALES_APPROVAL_TASK-->SALES_APPROVAL{Is ok?};
|
||||
SALES_APPROVAL-->|TRUE|CHECK_PRICES_ESCALATED{Over limit?};
|
||||
SALES_APPROVAL-->|FALSE|SALES_NOT_APPROVED[Inform OP];
|
||||
CHECK_PRICES_ESCALATED-->|TRUE|CREATE_CFO_PRICE_APPROVAL[CFO approval task];
|
||||
CHECK_PRICES_ESCALATED-->|FALSE|PRINTABLE;
|
||||
CREATE_CFO_PRICE_APPROVAL-->CFO_APPROVAL{Is ok?};
|
||||
CFO_APPROVAL-->|TRUE|PRINTABLE[Mark printable];
|
||||
CFO_APPROVAL-->|FALSE|CFO_NOT_APPROVED[Inform OP + Sales];
|
||||
|
||||
CLICK_PRINT[Click print]-->IS_APPROVED{Is approved};
|
||||
IS_APPROVED-->|TRUE|PRINT[Print];
|
||||
IS_APPROVED-->|FALSE|PRINT_ERROR[Show print error];
|
||||
|
||||
UPDATE_BILL[Update bill]-->CHECK_THREASHOLDS{Change above threshold};
|
||||
CHECK_THREASHOLDS-->|TRUE|OPEN_TASKS[Update & re-open tasks];
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="radio" id="c-tab-3" name="tabular-2"<?= empty($this->request->uri->fragment) || $this->request->uri->fragment === 'c-tab-1' ? ' checked' : ''; ?>>
|
||||
<div class="tab">
|
||||
<div class="row">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="radio" id="c-tab-4" name="tabular-2"<?= empty($this->request->uri->fragment) || $this->request->uri->fragment === 'c-tab-1' ? ' checked' : ''; ?>>
|
||||
<div class="tab">
|
||||
<div class="row">
|
||||
<div class="col-xs-12 col-md-6">
|
||||
<section class="portlet">
|
||||
<div class="portlet-head"><?= $this->getHtml('Media'); ?><i class="g-icon download btn end-xs">download</i></div>
|
||||
<table class="default sticky" id="invoice-item-list">
|
||||
<thead>
|
||||
<tr>
|
||||
<td>
|
||||
<td class="wf-100"><?= $this->getHtml('Name'); ?>
|
||||
<td><?= $this->getHtml('Type'); ?>
|
||||
<tbody>
|
||||
<?php foreach ($media as $file) :
|
||||
$url = $file->extension === 'collection'
|
||||
? UriFactory::build('{/base}/media/list?path=' . \rtrim($file->getVirtualPath(), '/') . '/' . $file->name)
|
||||
: UriFactory::build('{/base}/media/view?id=' . $file->id
|
||||
. '&path={?path}' . (
|
||||
$file->id === 0
|
||||
? '/' . $file->name
|
||||
: ''
|
||||
)
|
||||
);
|
||||
|
||||
?>
|
||||
<tr data-href="<?= $url; ?>">
|
||||
<td>
|
||||
<td><a href="<?= $url; ?>"><?= $file->name; ?></a>
|
||||
<td><a href="<?= $url; ?>"><?= $file->extension; ?></a>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
12
Admin/Install/Workflow/bill/workflow.json
Normal file
12
Admin/Install/Workflow/bill/workflow.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
[
|
||||
{
|
||||
"name": "IS_APPROVED",
|
||||
"title": "Is approved?",
|
||||
"hooks": [
|
||||
],
|
||||
"if": {
|
||||
"isApproved": true,
|
||||
"action": "print"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -907,6 +907,11 @@
|
|||
"null": false,
|
||||
"foreignTable": "unit",
|
||||
"foreignKey": "unit_id"
|
||||
},
|
||||
"billing_bill_approval": {
|
||||
"name": "billing_bill_approval",
|
||||
"type": "INT",
|
||||
"null": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
@ -1278,6 +1283,11 @@
|
|||
"type": "INT",
|
||||
"default": null,
|
||||
"null": true
|
||||
},
|
||||
"billing_bill_element_approval": {
|
||||
"name": "billing_bill_element_approval",
|
||||
"type": "INT",
|
||||
"null": false
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -243,7 +243,7 @@ final class ApiAttributeController extends Controller
|
|||
->with('type')
|
||||
->with('type/defaults')
|
||||
->with('value')
|
||||
->where('id', (int) $request->getData('id'))
|
||||
->where('id', $request->getDataInt('id') ?? 0)
|
||||
->execute();
|
||||
|
||||
$new = $this->updateAttributeFromRequest($request, clone $old);
|
||||
|
|
@ -291,7 +291,7 @@ final class ApiAttributeController extends Controller
|
|||
|
||||
$billAttribute = BillAttributeMapper::get()
|
||||
->with('type')
|
||||
->where('id', (int) $request->getData('id'))
|
||||
->where('id', $request->getDataInt('id') ?? 0)
|
||||
->execute();
|
||||
|
||||
if ($billAttribute->type->isRequired) {
|
||||
|
|
@ -327,7 +327,7 @@ final class ApiAttributeController extends Controller
|
|||
}
|
||||
|
||||
/** @var BaseStringL11n $old */
|
||||
$old = BillAttributeTypeL11nMapper::get()->where('id', (int) $request->getData('id'))->execute();
|
||||
$old = BillAttributeTypeL11nMapper::get()->where('id', $request->getDataInt('id') ?? 0)->execute();
|
||||
$new = $this->updateAttributeTypeL11nFromRequest($request, clone $old);
|
||||
|
||||
$this->updateModel($request->header->account, $old, $new, BillAttributeTypeL11nMapper::class, 'bill_attribute_type_l11n', $request->getOrigin());
|
||||
|
|
@ -357,7 +357,7 @@ final class ApiAttributeController extends Controller
|
|||
}
|
||||
|
||||
/** @var BaseStringL11n $billAttributeTypeL11n */
|
||||
$billAttributeTypeL11n = BillAttributeTypeL11nMapper::get()->where('id', (int) $request->getData('id'))->execute();
|
||||
$billAttributeTypeL11n = BillAttributeTypeL11nMapper::get()->where('id', $request->getDataInt('id') ?? 0)->execute();
|
||||
$this->deleteModel($request->header->account, $billAttributeTypeL11n, BillAttributeTypeL11nMapper::class, 'bill_attribute_type_l11n', $request->getOrigin());
|
||||
$this->createStandardDeleteResponse($request, $response, $billAttributeTypeL11n);
|
||||
}
|
||||
|
|
@ -385,7 +385,7 @@ final class ApiAttributeController extends Controller
|
|||
}
|
||||
|
||||
/** @var AttributeType $old */
|
||||
$old = BillAttributeTypeMapper::get()->with('defaults')->where('id', (int) $request->getData('id'))->execute();
|
||||
$old = BillAttributeTypeMapper::get()->with('defaults')->where('id', $request->getDataInt('id') ?? 0)->execute();
|
||||
$new = $this->updateAttributeTypeFromRequest($request, clone $old);
|
||||
|
||||
$this->updateModel($request->header->account, $old, $new, BillAttributeTypeMapper::class, 'bill_attribute_type', $request->getOrigin());
|
||||
|
|
@ -417,7 +417,7 @@ final class ApiAttributeController extends Controller
|
|||
}
|
||||
|
||||
/** @var AttributeType $billAttributeType */
|
||||
$billAttributeType = BillAttributeTypeMapper::get()->with('defaults')->where('id', (int) $request->getData('id'))->execute();
|
||||
$billAttributeType = BillAttributeTypeMapper::get()->with('defaults')->where('id', $request->getDataInt('id') ?? 0)->execute();
|
||||
$this->deleteModel($request->header->account, $billAttributeType, BillAttributeTypeMapper::class, 'bill_attribute_type', $request->getOrigin());
|
||||
$this->createStandardDeleteResponse($request, $response, $billAttributeType);
|
||||
}
|
||||
|
|
@ -445,7 +445,7 @@ final class ApiAttributeController extends Controller
|
|||
}
|
||||
|
||||
/** @var AttributeValue $old */
|
||||
$old = BillAttributeValueMapper::get()->where('id', (int) $request->getData('id'))->execute();
|
||||
$old = BillAttributeValueMapper::get()->where('id', $request->getDataInt('id') ?? 0)->execute();
|
||||
|
||||
/** @var \Modules\Attribute\Models\Attribute $attr */
|
||||
$attr = BillAttributeMapper::get()
|
||||
|
|
@ -485,7 +485,7 @@ final class ApiAttributeController extends Controller
|
|||
// }
|
||||
|
||||
// /** @var \Modules\Billing\Models\BillAttributeValue $billAttributeValue */
|
||||
// $billAttributeValue = BillAttributeValueMapper::get()->where('id', (int) $request->getData('id'))->execute();
|
||||
// $billAttributeValue = BillAttributeValueMapper::get()->where('id', $request->getDataInt('id') ?? 0)->execute();
|
||||
// $this->deleteModel($request->header->account, $billAttributeValue, BillAttributeValueMapper::class, 'bill_attribute_value', $request->getOrigin());
|
||||
// $this->createStandardDeleteResponse($request, $response, $billAttributeValue);
|
||||
}
|
||||
|
|
@ -513,7 +513,7 @@ final class ApiAttributeController extends Controller
|
|||
}
|
||||
|
||||
/** @var BaseStringL11n $old */
|
||||
$old = BillAttributeValueL11nMapper::get()->where('id', (int) $request->getData('id'));
|
||||
$old = BillAttributeValueL11nMapper::get()->where('id', $request->getDataInt('id') ?? 0);
|
||||
$new = $this->updateAttributeValueL11nFromRequest($request, clone $old);
|
||||
|
||||
$this->updateModel($request->header->account, $old, $new, BillAttributeValueL11nMapper::class, 'bill_attribute_value_l11n', $request->getOrigin());
|
||||
|
|
@ -543,7 +543,7 @@ final class ApiAttributeController extends Controller
|
|||
}
|
||||
|
||||
/** @var BaseStringL11n $billAttributeValueL11n */
|
||||
$billAttributeValueL11n = BillAttributeValueL11nMapper::get()->where('id', (int) $request->getData('id'))->execute();
|
||||
$billAttributeValueL11n = BillAttributeValueL11nMapper::get()->where('id', $request->getDataInt('id') ?? 0)->execute();
|
||||
$this->deleteModel($request->header->account, $billAttributeValueL11n, BillAttributeValueL11nMapper::class, 'bill_attribute_value_l11n', $request->getOrigin());
|
||||
$this->createStandardDeleteResponse($request, $response, $billAttributeValueL11n);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,10 @@ use phpOMS\Model\Message\FormValidation;
|
|||
use phpOMS\Stdlib\Base\FloatInt;
|
||||
use phpOMS\System\MimeType;
|
||||
use phpOMS\Views\View;
|
||||
use Modules\Billing\Models\WorkflowType;
|
||||
use Modules\Workflow\Models\WorkflowTemplateMapper;
|
||||
use Modules\Workflow\Models\WorkflowTemplateStatus;
|
||||
use phpOMS\Message\Http\HttpResponse;
|
||||
|
||||
/**
|
||||
* Billing class.
|
||||
|
|
@ -278,7 +282,7 @@ final class ApiBillController extends Controller
|
|||
return;
|
||||
}
|
||||
|
||||
$this->app->eventManager->triggerSimilar('PRE:Module:' . self::NAME . '-bill-finalize', '', [
|
||||
$this->app->eventManager->triggerSimilar('PRE:' . self::NAME . '-bill-finalize', '', [
|
||||
$request->header->account,
|
||||
null, $new,
|
||||
null, self::NAME . '-bill-finalize',
|
||||
|
|
@ -317,7 +321,7 @@ final class ApiBillController extends Controller
|
|||
}
|
||||
|
||||
/** @var \Modules\Billing\Models\Bill $old */
|
||||
$old = BillMapper::get()->where('id', (int) $request->getData('bill'))->execute();
|
||||
$old = BillMapper::get()->where('id', $request->getDataInt('bill') ?? 0)->execute();
|
||||
|
||||
// @feature Allow to update internal statistical fields
|
||||
// Example: Referral account
|
||||
|
|
@ -482,7 +486,7 @@ final class ApiBillController extends Controller
|
|||
$bill->accSection = empty($temp = $bill->client->getAttribute('section')->value->id) ? null : $temp;
|
||||
$bill->accGroup = empty($temp = $bill->client->getAttribute('client_group')->value->id) ? null : $temp;
|
||||
$bill->accType = empty($temp = $bill->client->getAttribute('client_type')->value->id) ? null : $temp;
|
||||
$bill->rep = $request->hasData('rep') ? new NullSalesRep((int) $request->getData('rep')) : $account->rep;
|
||||
$bill->rep = $request->hasData('rep') ? new NullSalesRep($request->getDataInt('re ?? 0p')) : $account->rep;
|
||||
} else {
|
||||
$bill->supplier = $account;
|
||||
$bill->accTaxCode = empty($temp = $bill->supplier->getAttribute('purchase_tax_code')->value->id) ? null : $temp;
|
||||
|
|
@ -602,7 +606,7 @@ final class ApiBillController extends Controller
|
|||
$attr->value = $attrValue;
|
||||
|
||||
$container = $request->hasData('container')
|
||||
? new NullContainer((int) $request->getData('container'))
|
||||
? new NullContainer($request->getDataInt('co ?? 0ntainer'))
|
||||
: null;
|
||||
|
||||
$attr = new NullAttribute();
|
||||
|
|
@ -723,7 +727,7 @@ final class ApiBillController extends Controller
|
|||
->with('attributes')
|
||||
->with('attributes/type')
|
||||
->with('attributes/value')
|
||||
->where('id', (int) $request->getData('client'))
|
||||
->where('id', $request->getDataInt('client') ?? 0)
|
||||
->where('attributes/type/name', [
|
||||
'segment', 'section', 'client_group', 'client_type',
|
||||
'sales_tax_code',
|
||||
|
|
@ -740,7 +744,7 @@ final class ApiBillController extends Controller
|
|||
->with('attributes')
|
||||
->with('attributes/type')
|
||||
->with('attributes/value')
|
||||
->where('id', (int) $request->getData('supplier'))
|
||||
->where('id', $request->getDataInt('supplier') ?? 0)
|
||||
->where('attributes/type/name', [
|
||||
'purchase_tax_code',
|
||||
], 'IN')
|
||||
|
|
@ -797,7 +801,7 @@ final class ApiBillController extends Controller
|
|||
}
|
||||
|
||||
/** @var \Modules\Billing\Models\Bill $bill */
|
||||
$bill = BillMapper::get()->where('id', (int) $request->getData('ref'))->execute();
|
||||
$bill = BillMapper::get()->where('id', $request->getDataInt('ref') ?? 0)->execute();
|
||||
$path = $this->createBillDir($bill);
|
||||
|
||||
$uploaded = new NullCollection();
|
||||
|
|
@ -864,10 +868,10 @@ final class ApiBillController extends Controller
|
|||
}
|
||||
|
||||
/** @var \Modules\Media\Models\Media $media */
|
||||
$media = MediaMapper::get()->where('id', (int) $request->getData('media'))->execute();
|
||||
$media = MediaMapper::get()->where('id', $request->getDataInt('media') ?? 0)->execute();
|
||||
|
||||
/** @var \Modules\Billing\Models\Bill $bill */
|
||||
$bill = BillMapper::get()->where('id', (int) $request->getData('bill'))->execute();
|
||||
$bill = BillMapper::get()->where('id', $request->getDataInt('bill') ?? 0)->execute();
|
||||
|
||||
// Cannot delete system generated bill
|
||||
if (\stripos($media->name, $bill->number) !== false) {
|
||||
|
|
@ -1593,7 +1597,7 @@ final class ApiBillController extends Controller
|
|||
}
|
||||
|
||||
/** @var \Modules\Billing\Models\Bill $bill */
|
||||
$bill = BillMapper::get()->where('id', (int) $request->getData('ref'))->execute();
|
||||
$bill = BillMapper::get()->where('id', $request->getDataInt('ref') ?? 0)->execute();
|
||||
|
||||
$request->setData('virtualpath', $this->createBillDir($bill), true);
|
||||
$this->app->moduleManager->get('Editor', 'Api')->apiEditorCreate($request, $response, $data);
|
||||
|
|
@ -1659,7 +1663,7 @@ final class ApiBillController extends Controller
|
|||
}
|
||||
|
||||
/** @var \Modules\Billing\Models\Bill $old */
|
||||
$old = BillMapper::get()->where('id', (int) $request->getData('id'))->execute();
|
||||
$old = BillMapper::get()->where('id', $request->getDataInt('id') ?? 0)->execute();
|
||||
|
||||
// @todo check if bill can be deleted
|
||||
// @todo adjust stock transfer
|
||||
|
|
@ -1730,7 +1734,7 @@ final class ApiBillController extends Controller
|
|||
/** @var BillElement $old */
|
||||
$old = BillElementMapper::get()
|
||||
->with('bill')
|
||||
->where('id', (int) $request->getData('id'))
|
||||
->where('id', $request->getDataInt('id') ?? 0)
|
||||
->execute();
|
||||
|
||||
if ($old->bill->status === BillStatus::ARCHIVED) {
|
||||
|
|
@ -1813,7 +1817,7 @@ final class ApiBillController extends Controller
|
|||
// @todo handle transactions and bill update
|
||||
|
||||
/** @var \Modules\Billing\Models\BillElement $billElement */
|
||||
$billElement = BillElementMapper::get()->where('id', (int) $request->getData('id'))->execute();
|
||||
$billElement = BillElementMapper::get()->where('id', $request->getDataInt('id') ?? 0)->execute();
|
||||
$this->deleteModel($request->header->account, $billElement, BillElementMapper::class, 'bill_element', $request->getOrigin());
|
||||
$this->createStandardDeleteResponse($request, $response, $billElement);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -208,7 +208,7 @@ final class ApiBillTypeController extends Controller
|
|||
}
|
||||
|
||||
/** @var BillType $old */
|
||||
$old = BillTypeMapper::get()->where('id', (int) $request->getData('id'));
|
||||
$old = BillTypeMapper::get()->where('id', $request->getDataInt('id') ?? 0);
|
||||
$new = $this->updateBillTypeFromRequest($request, clone $old);
|
||||
|
||||
$this->updateModel($request->header->account, $old, $new, BillTypeMapper::class, 'bill_type', $request->getOrigin());
|
||||
|
|
@ -285,7 +285,7 @@ final class ApiBillTypeController extends Controller
|
|||
}
|
||||
|
||||
/** @var \Modules\Billing\Models\BillType $billType */
|
||||
$billType = BillTypeMapper::get()->where('id', (int) $request->getData('id'))->execute();
|
||||
$billType = BillTypeMapper::get()->where('id', $request->getDataInt('id') ?? 0)->execute();
|
||||
$this->deleteModel($request->header->account, $billType, BillTypeMapper::class, 'bill_type', $request->getOrigin());
|
||||
$this->createStandardDeleteResponse($request, $response, $billType);
|
||||
}
|
||||
|
|
@ -332,7 +332,7 @@ final class ApiBillTypeController extends Controller
|
|||
}
|
||||
|
||||
/** @var BaseStringL11n $old */
|
||||
$old = BillTypeL11nMapper::get()->where('id', (int) $request->getData('id'));
|
||||
$old = BillTypeL11nMapper::get()->where('id', $request->getDataInt('id') ?? 0);
|
||||
$new = $this->updateBillTypeL11nFromRequest($request, clone $old);
|
||||
|
||||
$this->updateModel($request->header->account, $old, $new, BillTypeL11nMapper::class, 'bill_type_l11n', $request->getOrigin());
|
||||
|
|
@ -400,7 +400,7 @@ final class ApiBillTypeController extends Controller
|
|||
}
|
||||
|
||||
/** @var BaseStringL11n $billTypeL11n */
|
||||
$billTypeL11n = BillTypeL11nMapper::get()->where('id', (int) $request->getData('id'))->execute();
|
||||
$billTypeL11n = BillTypeL11nMapper::get()->where('id', $request->getDataInt('id') ?? 0)->execute();
|
||||
$this->deleteModel($request->header->account, $billTypeL11n, BillTypeL11nMapper::class, 'bill_type_l11n', $request->getOrigin());
|
||||
$this->createStandardDeleteResponse($request, $response, $billTypeL11n);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -486,7 +486,7 @@ final class ApiPriceController extends Controller
|
|||
}
|
||||
|
||||
/** @var \Modules\Billing\Models\Price\Price $old */
|
||||
$old = PriceMapper::get()->where('id', (int) $request->getData('id'))->execute();
|
||||
$old = PriceMapper::get()->where('id', $request->getDataInt('id') ?? 0)->execute();
|
||||
$new = $this->updatePriceFromRequest($request, clone $old);
|
||||
|
||||
$this->app->cachePool->get()->delete(
|
||||
|
|
@ -605,7 +605,7 @@ final class ApiPriceController extends Controller
|
|||
}
|
||||
|
||||
/** @var \Modules\Billing\Models\Price\Price $price */
|
||||
$price = PriceMapper::get()->where('id', (int) $request->getData('id'))->execute();
|
||||
$price = PriceMapper::get()->where('id', $request->getDataInt('id') ?? 0)->execute();
|
||||
|
||||
if ($price->name === 'default') {
|
||||
// default price cannot be deleted
|
||||
|
|
|
|||
|
|
@ -143,6 +143,9 @@ final class ApiPurchaseController extends Controller
|
|||
}
|
||||
*/
|
||||
|
||||
// @question How do we allow to manually assign a bill to a person/group for approval?
|
||||
// Possible solution: create general module called Approval for all kinds of approvals
|
||||
|
||||
$documents = $files;
|
||||
|
||||
foreach ($documents as $file) {
|
||||
|
|
@ -255,7 +258,7 @@ final class ApiPurchaseController extends Controller
|
|||
}
|
||||
|
||||
$bill = BillMapper::get()
|
||||
->where('id', (int) $request->getData('id'))
|
||||
->where('id', $request->getDataInt('id') ?? 0)
|
||||
->execute();
|
||||
|
||||
// After a bill is "closed" its values shouldn't change
|
||||
|
|
|
|||
|
|
@ -304,7 +304,7 @@ final class ApiTaxController extends Controller
|
|||
}
|
||||
|
||||
/** @var \Modules\Billing\Models\Tax\TaxCombination $old */
|
||||
$old = TaxCombinationMapper::get()->where('id', (int) $request->getData('id'))->execute();
|
||||
$old = TaxCombinationMapper::get()->where('id', $request->getDataInt('id') ?? 0)->execute();
|
||||
$new = $this->updateTaxCombinationFromRequest($request, clone $old);
|
||||
|
||||
$this->updateModel($request->header->account, $old, $new, TaxCombinationMapper::class, 'tax_combination', $request->getOrigin());
|
||||
|
|
@ -463,7 +463,7 @@ final class ApiTaxController extends Controller
|
|||
}
|
||||
|
||||
/** @var \Modules\Billing\Models\Tax\TaxCombination $taxCombination */
|
||||
$taxCombination = TaxCombinationMapper::get()->where('id', (int) $request->getData('id'))->execute();
|
||||
$taxCombination = TaxCombinationMapper::get()->where('id', $request->getDataInt('id') ?? 0)->execute();
|
||||
$this->deleteModel($request->header->account, $taxCombination, TaxCombinationMapper::class, 'tax_combination', $request->getOrigin());
|
||||
$this->createStandardDeleteResponse($request, $response, $taxCombination);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -246,7 +246,7 @@ final class BackendController extends Controller
|
|||
->with('files')
|
||||
->with('files/tags')
|
||||
->with('notes')
|
||||
->where('id', (int) $request->getData('id'))
|
||||
->where('id', $request->getDataInt('id') ?? 0)
|
||||
->execute();
|
||||
|
||||
$view->data['billtypes'] = BillTypeMapper::getAll()
|
||||
|
|
@ -442,7 +442,7 @@ final class BackendController extends Controller
|
|||
->with('files')
|
||||
->with('files/tags')
|
||||
->with('notes')
|
||||
->where('id', (int) $request->getData('id'))
|
||||
->where('id', $request->getDataInt('id') ?? 0)
|
||||
->execute();
|
||||
|
||||
$view->data['billtypes'] = BillTypeMapper::getAll()
|
||||
|
|
@ -537,7 +537,7 @@ final class BackendController extends Controller
|
|||
$view->setTemplate('/Modules/Billing/Theme/Backend/purchase-bill');
|
||||
$view->data['nav'] = $this->app->moduleManager->get('Navigation')->createNavigationMid(1005106001, $request, $response);
|
||||
|
||||
$bill = BillMapper::get()->where('id', (int) $request->getData('id'))->execute();
|
||||
$bill = BillMapper::get()->where('id', $request->getDataInt('id') ?? 0)->execute();
|
||||
|
||||
$view->data['bill'] = $bill;
|
||||
$view->data['media-upload'] = new \Modules\Media\Theme\Backend\Components\Upload\BaseView($this->app->l11nManager, $request, $response);
|
||||
|
|
@ -647,7 +647,7 @@ final class BackendController extends Controller
|
|||
->with('files')
|
||||
->with('files/tags')
|
||||
->with('notes')
|
||||
->where('id', (int) $request->getData('id'))
|
||||
->where('id', $request->getDataInt('id') ?? 0)
|
||||
->execute();
|
||||
|
||||
$tags = TagMapper::getAll()
|
||||
|
|
@ -713,7 +713,7 @@ final class BackendController extends Controller
|
|||
|
||||
$view->data['type'] = PaymentTermMapper::get()
|
||||
->with('l11n')
|
||||
->where('id', (int) $request->getData('id'))
|
||||
->where('id', $request->getDataInt('id') ?? 0)
|
||||
->where('l11n/language', $response->header->l11n->language)
|
||||
->execute();
|
||||
|
||||
|
|
@ -775,7 +775,7 @@ final class BackendController extends Controller
|
|||
|
||||
$view->data['type'] = ShippingTermMapper::get()
|
||||
->with('l11n')
|
||||
->where('id', (int) $request->getData('id'))
|
||||
->where('id', $request->getDataInt('id') ?? 0)
|
||||
->where('l11n/language', $response->header->l11n->language)
|
||||
->execute();
|
||||
|
||||
|
|
@ -887,7 +887,7 @@ final class BackendController extends Controller
|
|||
->with('supplierCode')
|
||||
->with('itemCode')
|
||||
->with('taxCode')
|
||||
->where('id', (int) $request->getData('id'))
|
||||
->where('id', $request->getDataInt('id') ?? 0)
|
||||
->execute();
|
||||
|
||||
$view->data['client_codes'] = ClientAttributeTypeMapper::get()
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Modules\Billing\Controller;
|
||||
|
||||
use Modules\Admin\Models\AccountMapper;
|
||||
use Modules\Billing\Models\BillElement;
|
||||
use Modules\Billing\Models\BillElementMapper;
|
||||
use Modules\Billing\Models\BillMapper;
|
||||
|
|
@ -29,6 +30,7 @@ use Modules\SupplierManagement\Models\NullSupplier;
|
|||
use Modules\SupplierManagement\Models\Supplier;
|
||||
use Modules\SupplierManagement\Models\SupplierMapper;
|
||||
use Modules\Tag\Models\TagMapper;
|
||||
use phpOMS\Account\AccountStatus;
|
||||
use phpOMS\Contract\RenderableInterface;
|
||||
use phpOMS\Localization\ISO4217CharEnum;
|
||||
use phpOMS\Localization\ISO4217DecimalEnum;
|
||||
|
|
@ -121,12 +123,22 @@ final class CliController extends Controller
|
|||
$identifiers = \json_decode($identifierContent, true);
|
||||
|
||||
/* Supplier */
|
||||
// @performance Do we really want to select all the attributes below or only after we have found a suitable supplier
|
||||
// We don't need these attributes initially, only once we found a matching supplier
|
||||
|
||||
// @performance We can't select all suppliers in one go, we probably need to iterate in chunks
|
||||
|
||||
// @bug We are missing the payment information here used in the matchSupplier() function
|
||||
|
||||
// @performance Could it be better to first perform some parsing of the bill to get the payment information and find the supplier based on that first?
|
||||
// If we find a supplier this would be much faster, if it doesn't we can still do the brute force below
|
||||
|
||||
/** @var \Modules\SupplierManagement\Models\Supplier[] $suppliers */
|
||||
$suppliers = SupplierMapper::getAll()
|
||||
->with('account')
|
||||
->with('mainAddress')
|
||||
->with('attributes/type')
|
||||
->where('attributes/type/name', ['bill_match_pattern', 'bill_date_format'], 'IN')
|
||||
->where('attributes/type/name', ['bill_match_pattern', 'bill_date_format', 'bill_approval'], 'IN')
|
||||
->executeGetArray();
|
||||
|
||||
$bill->supplier = $this->matchSupplier($content, $suppliers);
|
||||
|
|
@ -482,6 +494,18 @@ final class CliController extends Controller
|
|||
$billResponse = new HttpResponse();
|
||||
$this->app->moduleManager->get('Billing', 'ApiBill')->apiBillPdfArchiveCreate($request, $billResponse);
|
||||
|
||||
if ($bill->supplier->id !== 0) {
|
||||
// @question Do we want to also create a notification for the people in the default group
|
||||
|
||||
/*
|
||||
$approvalAccounts = AccountMapper::getAll()
|
||||
->with('groups')
|
||||
->where('status', AccountStatus::ACTIVE)
|
||||
->where('groups/name', $bill->supplier->getAttribute('bill_approval')->value->valueStr)
|
||||
->executeGetArray();
|
||||
*/
|
||||
}
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ use Modules\Admin\Models\NullAccount;
|
|||
use Modules\ClientManagement\Models\Client;
|
||||
use Modules\Sales\Models\SalesRep;
|
||||
use Modules\SupplierManagement\Models\Supplier;
|
||||
use Modules\Workflow\Models\NullWorkflowStep;
|
||||
use Modules\Workflow\Models\WorkflowStep;
|
||||
use phpOMS\Localization\BaseStringL11nType;
|
||||
use phpOMS\Localization\ISO4217CharEnum;
|
||||
use phpOMS\Localization\ISO639x1Enum;
|
||||
|
|
@ -57,6 +59,8 @@ class Bill implements \JsonSerializable
|
|||
|
||||
public int $unit = 0;
|
||||
|
||||
public WorkflowStep $approval;
|
||||
|
||||
public int $source = 0;
|
||||
|
||||
/**
|
||||
|
|
@ -418,6 +422,7 @@ class Bill implements \JsonSerializable
|
|||
public ?string $fiAccount = null;
|
||||
|
||||
// @todo Implement reason for bill (especially useful for credit notes, warehouse bookings)
|
||||
// @todo Implement internal notes for bill
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
|
|
@ -438,6 +443,8 @@ class Bill implements \JsonSerializable
|
|||
$this->createdBy = new NullAccount();
|
||||
$this->referral = new NullAccount();
|
||||
$this->type = new NullBillType();
|
||||
|
||||
$this->approval = new NullWorkflowStep();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ use Modules\Billing\Models\Tax\TaxCombination;
|
|||
use Modules\ItemManagement\Models\Container;
|
||||
use Modules\ItemManagement\Models\Item;
|
||||
use Modules\ItemManagement\Models\NullItem;
|
||||
use Modules\Workflow\Models\NullWorkflowStep;
|
||||
use Modules\Workflow\Models\WorkflowStep;
|
||||
use phpOMS\Localization\ISO4217DecimalEnum;
|
||||
use phpOMS\Stdlib\Base\FloatInt;
|
||||
use phpOMS\Stdlib\Base\SmartDateTime;
|
||||
|
|
@ -42,6 +44,8 @@ class BillElement implements \JsonSerializable
|
|||
|
||||
public int $order = 0;
|
||||
|
||||
public WorkflowStep $approval;
|
||||
|
||||
public ?Item $item = null;
|
||||
|
||||
public ?Container $container = null;
|
||||
|
|
@ -201,6 +205,8 @@ class BillElement implements \JsonSerializable
|
|||
|
||||
$this->taxP = new FloatInt();
|
||||
$this->taxR = new FloatInt();
|
||||
|
||||
$this->approval = new NullWorkflowStep();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ namespace Modules\Billing\Models;
|
|||
use Modules\Billing\Models\Tax\TaxCombinationMapper;
|
||||
use Modules\ItemManagement\Models\ContainerMapper;
|
||||
use Modules\ItemManagement\Models\ItemMapper;
|
||||
use Modules\Workflow\Models\WorkflowStepMapper;
|
||||
use phpOMS\DataStorage\Database\Mapper\DataMapperFactory;
|
||||
|
||||
/**
|
||||
|
|
@ -80,6 +81,8 @@ final class BillElementMapper extends DataMapperFactory
|
|||
'billing_bill_element_fiaccount' => ['name' => 'billing_bill_element_fiaccount', 'type' => 'string', 'internal' => 'fiAccount'],
|
||||
'billing_bill_element_costcenter' => ['name' => 'billing_bill_element_costcenter', 'type' => 'string', 'internal' => 'costcenter'],
|
||||
'billing_bill_element_costobject' => ['name' => 'billing_bill_element_costobject', 'type' => 'string', 'internal' => 'costobject'],
|
||||
|
||||
'billing_bill_element_approval' => ['name' => 'billing_bill_element_approval', 'type' => 'int', 'internal' => 'approval'],
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -118,6 +121,10 @@ final class BillElementMapper extends DataMapperFactory
|
|||
'mapper' => TaxCombinationMapper::class,
|
||||
'external' => 'billing_bill_element_tax_combination',
|
||||
],
|
||||
'approval' => [
|
||||
'mapper' => WorkflowStepMapper::class,
|
||||
'external' => 'billing_bill_element_approval',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ use Modules\Editor\Models\EditorDocMapper;
|
|||
use Modules\Media\Models\MediaMapper;
|
||||
use Modules\Sales\Models\SalesRepMapper;
|
||||
use Modules\SupplierManagement\Models\SupplierMapper;
|
||||
use Modules\Workflow\Models\WorkflowStepMapper;
|
||||
use phpOMS\DataStorage\Database\Mapper\DataMapperFactory;
|
||||
|
||||
/**
|
||||
|
|
@ -100,6 +101,7 @@ class BillMapper extends DataMapperFactory
|
|||
'billing_bill_performance_date' => ['name' => 'billing_bill_performance_date', 'type' => 'DateTime', 'internal' => 'performanceDate', 'readonly' => true],
|
||||
'billing_bill_created_at' => ['name' => 'billing_bill_created_at', 'type' => 'DateTimeImmutable', 'internal' => 'createdAt', 'readonly' => true],
|
||||
'billing_bill_unit' => ['name' => 'billing_bill_unit', 'type' => 'int', 'internal' => 'unit'],
|
||||
'billing_bill_approval' => ['name' => 'billing_bill_approval', 'type' => 'int', 'internal' => 'approval'],
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -162,6 +164,10 @@ class BillMapper extends DataMapperFactory
|
|||
'mapper' => ShippingTermMapper::class,
|
||||
'external' => 'shippingTerms',
|
||||
],
|
||||
'approval' => [
|
||||
'mapper' => WorkflowStepMapper::class,
|
||||
'external' => 'approval',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ abstract class BillStatus extends Enum
|
|||
|
||||
public const DELETED = 4;
|
||||
|
||||
// Bill is completed and ready to be finalized
|
||||
// This means the bill can now be approved (if the workflow is defined that way)
|
||||
public const DONE = 8;
|
||||
|
||||
public const DRAFT = 16;
|
||||
|
|
|
|||
61
Models/WorkflowStepStatusEnum.php
Normal file
61
Models/WorkflowStepStatusEnum.php
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
/**
|
||||
* Jingga
|
||||
*
|
||||
* PHP Version 8.2
|
||||
*
|
||||
* @package Modules\Billing\Models
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 2.2
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\Billing\Models;
|
||||
|
||||
use phpOMS\Stdlib\Base\Enum;
|
||||
|
||||
/**
|
||||
* Workflow type enum.
|
||||
*
|
||||
* @package Modules\Billing\Models
|
||||
* @license OMS License 2.2
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
abstract class WorkflowStepStatusEnum extends Enum
|
||||
{
|
||||
// The price/profit of the element needs approval
|
||||
public const BILL_ELEMENT_PRICE_APPROVAL = 1 << 0;
|
||||
|
||||
// Large quantity of element is large and needs to be approved
|
||||
public const BILL_ELEMENT_QUANTITY_APPROVAL = 1 << 1;
|
||||
|
||||
// Large quantity of element is large and needs to be approved
|
||||
public const BILL_ELEMENT_PROFIT_APPROVAL = 1 << 2;
|
||||
|
||||
// The element is completely approved
|
||||
public const BILL_ELEMENT_APPROVAL = 1 << 3;
|
||||
|
||||
// The bill got checked for correctness
|
||||
public const BILL_CORRECTNESS_APPROVAL = 1 << 4;
|
||||
|
||||
// The total price is approved (e.g. large orders)
|
||||
public const BILL_PRICE_APPROVAL = 1 << 5;
|
||||
|
||||
// The profit of the bill is below a certain threshold
|
||||
public const BILL_PROFIT_APPROVAL = 1 << 6;
|
||||
|
||||
// The payment term is worse than the default term of the customer
|
||||
public const BILL_PAYMENT_TERM_APPROVAL = 1 << 7;
|
||||
|
||||
// Approval despite credit limit reached
|
||||
public const BILL_CREDIT_LIMIT_APPROVAL = 1 << 8;
|
||||
|
||||
// Allows bill to be paid
|
||||
public const BILL_PAYMENT_APPROVAL = 1 << 9;
|
||||
|
||||
// The bill is completely approved
|
||||
public const BILL_APPROVAL = 1 << 10;
|
||||
}
|
||||
32
Models/WorkflowType.php
Normal file
32
Models/WorkflowType.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
/**
|
||||
* Jingga
|
||||
*
|
||||
* PHP Version 8.2
|
||||
*
|
||||
* @package Modules\Billing\Models
|
||||
* @copyright Dennis Eichhorn
|
||||
* @license OMS License 2.2
|
||||
* @version 1.0.0
|
||||
* @link https://jingga.app
|
||||
*/
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Modules\Billing\Models;
|
||||
|
||||
use phpOMS\Stdlib\Base\Enum;
|
||||
|
||||
/**
|
||||
* Workflow type enum.
|
||||
*
|
||||
* @package Modules\Billing\Models
|
||||
* @license OMS License 2.2
|
||||
* @link https://jingga.app
|
||||
* @since 1.0.0
|
||||
*/
|
||||
abstract class WorkflowType extends Enum
|
||||
{
|
||||
public const SALES_BILL = 1;
|
||||
|
||||
public const PURCHASE_BILL = 2;
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ $currencies = ISO4217Enum::getConstants();
|
|||
$media = $this->data['media'] ?? [];
|
||||
|
||||
/** @var \Modules\Billing\Models\Bill $bill */
|
||||
$bill = $this->getData('bill') ?? new NullBill();
|
||||
$bill = $this->data['bill'] ?? new NullBill();
|
||||
$elements = $bill->elements;
|
||||
|
||||
$billTypes = $this->data['billtypes'] ?? [];
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
<?php declare(strict_types=1);
|
||||
|
||||
echo \json_encode($this->getData('bill') ?? null, \JSON_PRETTY_PRINT);
|
||||
echo \json_encode($this->data['bill'] ?? null, \JSON_PRETTY_PRINT);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user