This commit is contained in:
Dennis Eichhorn 2024-02-04 20:34:11 +00:00
parent 4d4e065fa1
commit 64e8499761
6 changed files with 169 additions and 91 deletions

View File

@ -230,16 +230,6 @@
"type": "BIGINT",
"null": false
},
"bizexpenses_expense_element_taxr": {
"name": "bizexpenses_expense_element_taxr",
"type": "BIGINT",
"null": false
},
"bizexpenses_expense_element_quantity": {
"name": "bizexpenses_expense_element_quantity",
"type": "BIGINT",
"null": false
},
"bizexpenses_expense_element_taxid": {
"name": "bizexpenses_expense_element_taxid",
"type": "TEXT",
@ -270,6 +260,12 @@
"foreignTable": "account",
"foreignKey": "account_id"
},
"bizexpenses_expense_element_bill": {
"name": "bizexpenses_expense_element_bill",
"type": "INT",
"null": true,
"default": null
},
"bizexpenses_expense_element_type": {
"name": "bizexpenses_expense_element_type",
"type": "INT",

View File

@ -26,8 +26,10 @@ use Modules\BusinessExpenses\Models\ExpenseMapper;
use Modules\BusinessExpenses\Models\ExpenseStatus;
use Modules\BusinessExpenses\Models\ExpenseTypeL11nMapper;
use Modules\BusinessExpenses\Models\ExpenseTypeMapper;
use Modules\BusinessExpenses\Models\MediaType;
use Modules\BusinessExpenses\Models\PermissionCategory;
use Modules\Media\Models\CollectionMapper;
use Modules\Media\Models\MediaClass;
use Modules\Media\Models\MediaMapper;
use Modules\Media\Models\PathSettings;
use Modules\SupplierManagement\Models\NullSupplier;
@ -36,6 +38,8 @@ use phpOMS\Localization\BaseStringL11n;
use phpOMS\Localization\BaseStringL11nType;
use phpOMS\Localization\ISO639x1Enum;
use phpOMS\Localization\NullBaseStringL11nType;
use phpOMS\Message\Http\HttpRequest;
use phpOMS\Message\Http\HttpResponse;
use phpOMS\Message\Http\RequestStatusCode;
use phpOMS\Message\NotificationLevel;
use phpOMS\Message\RequestAbstract;
@ -437,19 +441,48 @@ final class ApiController extends Controller
->execute();
$new = clone $old;
$new->recalculate();
$this->updateModel($request->header->account, $old, $new, ExpenseMapper::class, 'expense', $request->getOrigin());
if (!empty($request->files)) {
$request->setData('element', $element->id, true);
$this->apiMediaAddToExpenseElement($request, $response, $data);
// @todo refill element with parsed data from media (ocr)
}
$new->recalculate();
$this->updateModel($request->header->account, $old, $new, ExpenseMapper::class, 'expense', $request->getOrigin());
$this->createStandardCreateResponse($request, $response, $element);
}
public function apiExpenseElementFromUploadCreate(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void
{
if (!empty($val = $this->validateExpenseElementCreate($request))) {
$response->header->status = RequestStatusCode::R_400;
$this->createInvalidCreateResponse($request, $response, $val);
return;
}
$elements = [];
foreach ($request->files as $file) {
$internalResponse = new HttpResponse();
$internalRequest = new HttpRequest();
$internalRequest->header->account = $request->header->account;
$internalRequest->header->l11n = $request->header->l11n;
$internalRequest->setData('expense', $request->getDataInt('expense'));
$internalRequest->setData('file_type', MediaType::BILL);
$internalRequest->addFile($file);
$this->apiExpenseElementCreate($internalRequest, $internalResponse, $data);
$elements[] = $internalResponse->getDataArray($internalRequest->uri->__toString())['response'];
}
$this->createStandardCreateResponse($request, $response, $elements);
}
/**
* Method to create expense attribute l11n from request.
*
@ -471,10 +504,8 @@ final class ApiController extends Controller
// @todo handle different value set (net, gross, taxr, ...).
// Depending on the value set the other values should be calculated
$element->net = new FloatInt($request->getDataInt('net') ?? 0);
$element->taxR = new FloatInt($request->getDataInt('taxr') ?? 0);
$element->taxP = new FloatInt($request->getDataInt('taxp') ?? 0);
$element->gross = new FloatInt($request->getDataInt('gross') ?? 0);
$element->quantity = new FloatInt($request->getDataInt('quantity') ?? 0);
if ($request->hasData('supplier')) {
$element->supplier = new NullSupplier((int) $request->getData('supplier'));
@ -524,7 +555,7 @@ final class ApiController extends Controller
$uploaded = [];
if (!empty($uploadedFiles = $request->files)) {
$uploaded = $this->app->moduleManager->get('Media')->uploadFiles(
$uploaded = $this->app->moduleManager->get('Media', 'Api')->uploadFiles(
names: [],
fileNames: [],
files: $uploadedFiles,
@ -585,18 +616,17 @@ final class ApiController extends Controller
}
}
if (!empty($mediaFiles = $request->getDataJson('media'))) {
foreach ($mediaFiles as $media) {
$this->createModelRelation(
$request->header->account,
$expense->id,
(int) $media,
ExpenseElementMapper::class,
'files',
'',
$request->getOrigin()
);
}
$mediaFiles = $request->getDataJson('media');
foreach ($mediaFiles as $media) {
$this->createModelRelation(
$request->header->account,
$expense->id,
(int) $media,
ExpenseElementMapper::class,
'files',
'',
$request->getOrigin()
);
}
$this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Media', 'Media added to bill.', [
@ -656,7 +686,7 @@ final class ApiController extends Controller
$uploaded = [];
if (!empty($uploadedFiles = $request->files)) {
$uploaded = $this->app->moduleManager->get('Media')->uploadFiles(
$uploaded = $this->app->moduleManager->get('Media', 'Api')->uploadFiles(
names: [],
fileNames: [],
files: $uploadedFiles,
@ -717,24 +747,67 @@ final class ApiController extends Controller
}
}
if (!empty($mediaFiles = $request->getDataJson('media'))) {
foreach ($mediaFiles as $media) {
$this->createModelRelation(
$request->header->account,
$element,
(int) $media,
ExpenseElementMapper::class,
'files',
'',
$request->getOrigin()
);
}
$mediaFiles = $request->getDataJson('media');
foreach ($mediaFiles as $media) {
$this->createModelRelation(
$request->header->account,
$element,
(int) $media,
ExpenseElementMapper::class,
'files',
'',
$request->getOrigin()
);
}
$this->fillJsonResponse($request, $response, NotificationLevel::OK, 'Media', 'Media added to bill.', [
'upload' => $uploaded,
'media' => $mediaFiles,
]);
// Is invoice
if ($request->getDataString('file_type') === MediaType::BILL
&& \count($uploaded) + \count($mediaFiles) === 1
&& $this->app->moduleManager->isActive('Billing')
&& $expense->net->value !== 0
) {
$internalResponse = new HttpResponse();
$internalRequest = new HttpRequest();
$internalRequest->header->account = $request->header->account;
$internalRequest->header->l11n = $request->header->l11n;
$internalRequest->setData('media', empty($uploaded) ? \reset($mediaFiles) : \reset($uploaded)->id);
$internalRequest->setData('async', false);
$this->app->moduleManager->get('Billing', 'ApiPurchase')->apiSupplierBillUpload($internalRequest, $internalResponse, $data);
$bills = $internalResponse->getDataArray($internalRequest->uri->__toString())['response'];
$elementObj = ExpenseElementMapper::get()
->where('id', $element)
->execute();
$oldElement = clone $elementObj;
$elementObj->bill = \reset($bills);
$bill = \Modules\Billing\Models\BillMapper::get()
->where('id', $elementObj->bill)
->execute();
$elementObj->net = $bill->netSales;
$elementObj->taxP = $bill->taxP;
$elementObj->gross = $bill->grossSales;
$elementObj->supplier = $bill->supplier->id === 0 ? $bill->billTo : null;
$elementObj->country = $bill->billCountry;
$this->updateModel($request->header->account, $oldElement, $elementObj, ExpenseElementMapper::class, 'expense_element', $request->getOrigin());
}
$this->fillJsonResponse(
$request,
$response,
NotificationLevel::OK,
'Media', 'Media added to bill.',
[
'upload' => $uploaded,
'media' => $mediaFiles,
]);
}
/**
@ -774,7 +847,6 @@ final class ApiController extends Controller
*/
public function apiMediaRemoveFromExpenseElement(RequestAbstract $request, ResponseAbstract $response, array $data = []) : void
{
// @todo check that it is not system generated media!
if (!empty($val = $this->validateMediaRemoveFromExpenseElement($request))) {
$response->header->status = RequestStatusCode::R_400;
$this->createInvalidRemoveResponse($request, $response, $val);
@ -791,21 +863,28 @@ final class ApiController extends Controller
/** @var \Modules\BusinessExpenses\Models\ExpenseElement $element */
$element = ExpenseElementMapper::get()->where('id', (int) $request->getData('element'))->execute();
$path = $this->createExpenseDir($expense);
$path = \dirname($this->createExpenseDir($expense));
/** @var \Modules\Media\Models\Collection[] $elementCollection */
$elementCollection = CollectionMapper::getAll()
/** @var \Modules\Media\Models\Collection $collection */
$collection = CollectionMapper::get()
->where('name', (string) $element->id)
->where('virtual', $path)
->where('class', MediaClass::COLLECTION)
->limit(1)
->execute();
if (\count($elementCollection) !== 1) {
// For some reason there are multiple collections with the same virtual path?
// @todo check if this is the correct way to handle it or if we need to make sure that it is a collection
return;
if ($collection->id !== 0) {
$this->deleteModelRelation(
$request->header->account,
$collection->id,
$media->id,
CollectionMapper::class,
'sources',
'',
$request->getOrigin()
);
}
$collection = \reset($elementCollection);
$this->deleteModelRelation(
$request->header->account,
$element->id,
@ -816,23 +895,9 @@ final class ApiController extends Controller
$request->getOrigin()
);
$this->deleteModelRelation(
$request->header->account,
$collection->id,
$media->id,
CollectionMapper::class,
'sources',
'',
$request->getOrigin()
);
$referenceCount = MediaMapper::countInternalReferences($media->id);
if ($referenceCount === 0) {
// Is not used anywhere else -> remove from db and file system
// @todo remove media types from media
$this->deleteModel($request->header->account, $media, MediaMapper::class, 'element_media', $request->getOrigin());
if (\is_dir($media->getAbsolutePath())) {
@ -1259,8 +1324,6 @@ final class ApiController extends Controller
*
* @return array<string, bool>
*
* @todo Implement API validation function
*
* @since 1.0.0
*/
private function validateExpenseTypeL11nDelete(RequestAbstract $request) : array
@ -1379,8 +1442,6 @@ final class ApiController extends Controller
*
* @return array<string, bool>
*
* @todo Implement API validation function
*
* @since 1.0.0
*/
private function validateExpenseElementTypeDelete(RequestAbstract $request) : array
@ -1501,8 +1562,6 @@ final class ApiController extends Controller
*
* @return array<string, bool>
*
* @todo Implement API validation function
*
* @since 1.0.0
*/
private function validateExpenseElementTypeL11nDelete(RequestAbstract $request) : array
@ -1628,8 +1687,6 @@ final class ApiController extends Controller
*
* @return array<string, bool>
*
* @todo Implement API validation function
*
* @since 1.0.0
*/
private function validateExpenseDelete(RequestAbstract $request) : array
@ -1700,10 +1757,8 @@ final class ApiController extends Controller
// Depending on the value set the other values should be calculated
$new->net = $request->hasData('net') ? new FloatInt($request->getDataInt('net') ?? 0) : $new->net;
$new->taxR = $request->hasData('taxr') ? new FloatInt($request->getDataInt('taxr') ?? 0) : $new->taxR;
$new->taxP = $request->hasData('taxp') ? new FloatInt($request->getDataInt('taxp') ?? 0) : $new->taxP;
$new->gross = $request->hasData('gross') ? new FloatInt($request->getDataInt('gross') ?? 0) : $new->gross;
$new->quantity = $request->hasData('quantity') ? new FloatInt($request->getDataInt('quantity') ?? 0) : $new->quantity;
$new->supplier = $request->hasData('supplier') ? new NullSupplier((int) $request->getData('supplier')) : $new->supplier;
$new->country = $request->getDataString('country') ?? $new->country;
@ -1779,8 +1834,6 @@ final class ApiController extends Controller
*
* @return array<string, bool>
*
* @todo Implement API validation function
*
* @since 1.0.0
*/
private function validateExpenseElementDelete(RequestAbstract $request) : array

View File

@ -42,14 +42,10 @@ class ExpenseElement
public FloatInt $net;
public FloatInt $taxR;
public FloatInt $taxP;
public FloatInt $gross;
public FloatInt $quantity;
public BaseStringL11nType $type;
public ?Account $ref = null;
@ -64,6 +60,8 @@ class ExpenseElement
public \DateTime $end;
public ?int $bill = null;
/**
* Constructor.
*
@ -73,10 +71,8 @@ class ExpenseElement
{
$this->type = new BaseStringL11nType();
$this->net = new FloatInt();
$this->taxR = new FloatInt();
$this->taxP = new FloatInt();
$this->gross = new FloatInt();
$this->quantity = new FloatInt();
$this->start = new \DateTime('now');
$this->end = new \DateTime('now');
}

View File

@ -46,8 +46,6 @@ final class ExpenseElementMapper extends DataMapperFactory
'bizexpenses_expense_element_net' => ['name' => 'bizexpenses_expense_element_net', 'type' => 'Serializable', 'internal' => 'net'],
'bizexpenses_expense_element_gross' => ['name' => 'bizexpenses_expense_element_gross', 'type' => 'Serializable', 'internal' => 'gross'],
'bizexpenses_expense_element_taxp' => ['name' => 'bizexpenses_expense_element_taxp', 'type' => 'Serializable', 'internal' => 'taxP'],
'bizexpenses_expense_element_taxr' => ['name' => 'bizexpenses_expense_element_taxr', 'type' => 'Serializable', 'internal' => 'taxR'],
'bizexpenses_expense_element_quantity' => ['name' => 'bizexpenses_expense_element_quantity', 'type' => 'Serializable', 'internal' => 'quantity'],
'bizexpenses_expense_element_taxid' => ['name' => 'bizexpenses_expense_element_taxid', 'type' => 'string', 'internal' => 'taxId'],
'bizexpenses_expense_element_start' => ['name' => 'bizexpenses_expense_element_start', 'type' => 'DateTime', 'internal' => 'start'],
'bizexpenses_expense_element_end' => ['name' => 'bizexpenses_expense_element_end', 'type' => 'DateTime', 'internal' => 'end'],
@ -56,6 +54,7 @@ final class ExpenseElementMapper extends DataMapperFactory
'bizexpenses_expense_element_type' => ['name' => 'bizexpenses_expense_element_type', 'type' => 'int', 'internal' => 'type'],
'bizexpenses_expense_element_country' => ['name' => 'bizexpenses_expense_element_country', 'type' => 'string', 'internal' => 'country'],
'bizexpenses_expense_element_expense' => ['name' => 'bizexpenses_expense_element_expense', 'type' => 'int', 'internal' => 'expense'],
'bizexpenses_expense_element_bill' => ['name' => 'bizexpenses_expense_element_bill', 'type' => 'int', 'internal' => 'bill'],
];
/**

View File

@ -32,5 +32,7 @@ abstract class ExpenseStatus extends Enum
public const APPROVED = 3;
public const DENIED = 3;
public const DENIED = 4;
public const DELETED = 5;
}

32
Models/MediaType.php Normal file
View File

@ -0,0 +1,32 @@
<?php
/**
* Jingga
*
* PHP Version 8.1
*
* @package Modules\BusinessExpenses\Models
* @copyright Dennis Eichhorn
* @license OMS License 2.0
* @version 1.0.0
* @link https://jingga.app
*/
declare(strict_types=1);
namespace Modules\BusinessExpenses\Models;
use phpOMS\Stdlib\Base\Enum;
/**
* Expense status enum.
*
* @package Modules\BusinessExpenses\Models
* @license OMS License 2.0
* @link https://jingga.app
* @since 1.0.0
*/
abstract class MediaType extends Enum
{
public const BILL = 1;
public const OTHER = 2;
}