fixed multiple stuff
This commit is contained in:
@@ -323,11 +323,22 @@ class AddressModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists("company", $filter)) {
|
if (array_key_exists("company", $filter)) {
|
||||||
$company = FronkDB::singleton()->escape($filter["company"]);
|
$companyInput = trim($filter["company"]);
|
||||||
if ($company) {
|
if ($companyInput !== '') {
|
||||||
$where .= " AND company like '%$company%'";
|
$companyParts = preg_split('/\s+/', $companyInput);
|
||||||
|
$companyConditions = [];
|
||||||
|
foreach ($companyParts as $companyPart) {
|
||||||
|
$escapedCompanyPart = FronkDB::singleton()->escape($companyPart);
|
||||||
|
if ($escapedCompanyPart) {
|
||||||
|
$companyConditions[] = "company LIKE '%{$escapedCompanyPart}%'";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!empty($companyConditions)) {
|
||||||
|
$where .= " AND (" . implode(" AND ", $companyConditions) . ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (array_key_exists("firstname", $filter)) {
|
if (array_key_exists("firstname", $filter)) {
|
||||||
$firstname = FronkDB::singleton()->escape($filter["firstname"]);
|
$firstname = FronkDB::singleton()->escape($filter["firstname"]);
|
||||||
@@ -344,9 +355,19 @@ class AddressModel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (array_key_exists("mergedName", $filter)) {
|
if (array_key_exists("mergedName", $filter)) {
|
||||||
$name = FronkDB::singleton()->escape($filter["mergedName"]);
|
$mergedName = trim($filter["mergedName"]);
|
||||||
if ($name) {
|
if ($mergedName !== '') {
|
||||||
$where .= " AND (CONCAT(firstname, ' ', lastname) like '%$name%' OR CONCAT(lastname, ' ', firstname) like '%$name%' )";
|
$names = preg_split('/\s+/', $mergedName);
|
||||||
|
$conditions = [];
|
||||||
|
foreach ($names as $namePart) {
|
||||||
|
$escapedNamePart = FronkDB::singleton()->escape($namePart);
|
||||||
|
if ($escapedNamePart) {
|
||||||
|
$conditions[] = "(firstname LIKE '%{$escapedNamePart}%' OR lastname LIKE '%{$escapedNamePart}%')";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($conditions)) {
|
||||||
|
$where .= " AND (" . implode(" AND ", $conditions) . ")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
class WarehouseArticleController extends TTCrud {
|
class WarehouseArticleController extends TTCrud {
|
||||||
protected string $headerTitle = 'Artikel';
|
protected string $headerTitle = 'Artikel';
|
||||||
protected $createText = 'Artikel erstellen';
|
protected $createText = 'Artikel erstellen';
|
||||||
|
protected string $singleText = 'Artikel';
|
||||||
|
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
protected array $columns = [
|
protected array $columns = [
|
||||||
@@ -16,8 +17,8 @@ class WarehouseArticleController extends TTCrud {
|
|||||||
], 'table' => false], // Boolean value
|
], 'table' => false], // Boolean value
|
||||||
['key' => 'cheapestPurchasePrice', 'text' => 'Einkauf', 'modal' => false, 'table' => ['class' => 'text-center', 'suffix' => ' €']],
|
['key' => 'cheapestPurchasePrice', 'text' => 'Einkauf', 'modal' => false, 'table' => ['class' => 'text-center', 'suffix' => ' €']],
|
||||||
['key' => 'cheapestSellPrice', 'text' => 'Verkauf', 'modal' => false, 'table' => ['class' => 'text-center', 'suffix' => ' €']],
|
['key' => 'cheapestSellPrice', 'text' => 'Verkauf', 'modal' => false, 'table' => ['class' => 'text-center', 'suffix' => ' €']],
|
||||||
['key' => 'warningAmount', 'text' => 'Warnmenge', 'required' => true,'modal' => ['type' => 'number'], 'table' => ['class' => 'text-center']], // Stock/inventory related
|
['key' => 'warningAmount', 'text' => 'Warnmenge', 'required' => true,'modal' => ['type' => 'number'], 'table' => false], // Stock/inventory related
|
||||||
['key' => 'criticalAmount', 'text' => 'Kritische Menge', 'required' => true,'modal' => ['type' => 'number'], 'table' => ['class' => 'text-center']], // Stock/inventory related
|
['key' => 'criticalAmount', 'text' => 'Kritische Menge', 'required' => true,'modal' => ['type' => 'number'], 'table' => false], // Stock/inventory related
|
||||||
['key' => 'isSerialDocumentation', 'text' => 'Seriennummern', 'required' => false,'modal' => ['type' => 'checkbox'], 'table' => false], // Boolean value
|
['key' => 'isSerialDocumentation', 'text' => 'Seriennummern', 'required' => false,'modal' => ['type' => 'checkbox'], 'table' => false], // Boolean value
|
||||||
['key' => 'isEShop', 'text' => 'Ist E-Shop', 'required' => false,'modal' => ['type' => 'checkbox'], 'table' => false], // Boolean value
|
['key' => 'isEShop', 'text' => 'Ist E-Shop', 'required' => false,'modal' => ['type' => 'checkbox'], 'table' => false], // Boolean value
|
||||||
['key' => 'isEShopHide', 'text' => 'E-Shop Versteckt', 'required' => false,'modal' => ['type' => 'checkbox'], 'table' => false], // Boolean value
|
['key' => 'isEShopHide', 'text' => 'E-Shop Versteckt', 'required' => false,'modal' => ['type' => 'checkbox'], 'table' => false], // Boolean value
|
||||||
@@ -26,34 +27,19 @@ class WarehouseArticleController extends TTCrud {
|
|||||||
|
|
||||||
protected array $autocompleteColumns = ['articleNumber', 'title', 'description', 'category'];
|
protected array $autocompleteColumns = ['articleNumber', 'title', 'description', 'category'];
|
||||||
|
|
||||||
protected array $additionalActions = [
|
protected array $additionalActions = [['key' => 'openHistory','title' => 'Historie','class' => 'fas fa-history text-secondary']];
|
||||||
['key' => 'openHistory','title' => 'Historie','class' => 'fas fa-history text-secondary'],
|
|
||||||
['key' => 'editDistributorEntries','title' => 'Lieferanten','class' => 'fas fa-truck text-cyan'],
|
|
||||||
['key' => 'editThresholdEntries','title' => 'Schwellenwerte','class' => 'far fa-fw fa-box-full text-orange'],
|
|
||||||
['key' => 'editPricesEntries','title' => 'Preise','class' => 'fas fa-euro-sign text-green'],
|
|
||||||
['key' => 'addToCart','title' => 'Zur Bestellung hinzufügen','class' => 'fas fa-shopping-cart text-primary'],
|
|
||||||
];
|
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
protected array $additionalJSVariables = ['WAREHOUSE_ADMIN' => true];
|
protected array $additionalJSVariables = ['WAREHOUSE_ADMIN' => true];
|
||||||
|
|
||||||
protected array $infoMessages = ['create' => 'Artikel wurde erstellt',
|
|
||||||
'update' => 'Artikel wurde aktualisiert',
|
|
||||||
'delete' => 'Artikel wurde gelöscht',
|
|
||||||
'noChanges' => 'Keine Änderungen',];
|
|
||||||
|
|
||||||
protected function prepareCrudConfig() {
|
protected function prepareCrudConfig() {
|
||||||
if (!$this->user->can('WarehouseAdmin')) {
|
if ($this->user->can('WarehouseAdmin')) return;
|
||||||
// find column with key actions, cheapestPurchasePrice, warningAmount, criticalAmount and set table to false
|
|
||||||
foreach ($this->columns as $key => $column) {
|
array_walk($this->columns, fn(&$col) => in_array($col['key'], ['actions', 'cheapestPurchasePrice', 'warningAmount', 'criticalAmount']) && $col['table'] = false);
|
||||||
if (in_array($column['key'], ['actions', 'cheapestPurchasePrice', 'warningAmount', 'criticalAmount'])) {
|
|
||||||
$this->columns[$key]['table'] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->createText = false;
|
$this->createText = false;
|
||||||
$this->additionalJSVariables['WAREHOUSE_ADMIN'] = false;
|
$this->additionalJSVariables['WAREHOUSE_ADMIN'] = false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
protected function beforeUpdate($postData): bool {
|
protected function beforeUpdate($postData): bool {
|
||||||
(new WarehouseHistoryController)->create($postData, $this->mod);
|
(new WarehouseHistoryController)->create($postData, $this->mod);
|
||||||
@@ -64,28 +50,11 @@ class WarehouseArticleController extends TTCrud {
|
|||||||
self::updateCheapestPurchasePrice($postData['id']);
|
self::updateCheapestPurchasePrice($postData['id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static function updateCheapestPurchasePrice(int $id): void {
|
||||||
* Updates the cheapest purchase price for a given article from WarehouseArticleDistributorModel prices.
|
$article = WarehouseArticleModel::get($id);
|
||||||
*
|
if (!$article instanceof WarehouseArticleModel) throw new Exception("Invalid article type");
|
||||||
* @param int $articleId The ID of the article to update.
|
if (($distributor = WarehouseArticleDistributorModel::getAll(['articleId' => $id], 1, 0, ['key' => 'purchasePrice', 'order' => 'ASC'])) && $article->cheapestPurchasePrice != $distributor[0]->purchasePrice)
|
||||||
* @return void
|
WarehouseArticleModel::update(array_merge(get_object_vars($article), ['cheapestPurchasePrice' => $distributor[0]->purchasePrice]));
|
||||||
* @throws Exception If the article is not an instance of WarehouseArticleModel.
|
|
||||||
*/
|
|
||||||
public static function updateCheapestPurchasePrice(int $articleId): void {
|
|
||||||
$article = WarehouseArticleModel::get($articleId);
|
|
||||||
|
|
||||||
if (!$article instanceof WarehouseArticleModel) {
|
|
||||||
throw new Exception("Article is not an instance of WarehouseArticleModel");
|
|
||||||
}
|
|
||||||
|
|
||||||
$order = ['key' => 'purchasePrice', 'order' => 'ASC'];
|
|
||||||
$cheapestDistributorEntry = WarehouseArticleDistributorModel::getAll(['articleId' => $articleId], 1, 0, $order);
|
|
||||||
|
|
||||||
if (empty($cheapestDistributorEntry)) return;
|
|
||||||
|
|
||||||
$cheapestPurchasePrice = $cheapestDistributorEntry[0]->purchasePrice;
|
|
||||||
if ($article->cheapestPurchasePrice == $cheapestPurchasePrice) return;
|
|
||||||
WarehouseArticleModel::update(array_merge(get_object_vars($article), ['cheapestPurchasePrice' => $cheapestPurchasePrice]));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function afterCreate($postData) {
|
protected function afterCreate($postData) {
|
||||||
@@ -93,42 +62,24 @@ class WarehouseArticleController extends TTCrud {
|
|||||||
self::updateSellPrices($postData['id']);
|
self::updateSellPrices($postData['id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static function updateSellPrices(int $id): void { // Added return type hint
|
||||||
* Updates the sell prices for a given article.
|
$a = WarehouseArticleModel::get($id);
|
||||||
*
|
if (!$a instanceof WarehouseArticleModel) throw new Exception("Invalid article type");
|
||||||
* @param int $articleId The ID of the article to update.
|
|
||||||
* @return void
|
$aptsById = array_column(WarehouseArticlePriceModel::getAll(['articleId' => $id]), null, 'articlePriceTypeId');
|
||||||
* @throws Exception If the article is not an instance of WarehouseArticleModel.
|
$prices = [];
|
||||||
*/
|
$cpp = $a->cheapestPurchasePrice;
|
||||||
public static function updateSellPrices(int $articleId) {
|
|
||||||
$article = WarehouseArticleModel::get($articleId);
|
foreach (WarehouseArticlePriceTypeModel::getAll() as $pt) {
|
||||||
if (!$article instanceof WarehouseArticleModel) {
|
$apt = $aptsById[$pt->id] ?? null;
|
||||||
throw new Exception("Article is not an instance of WarehouseArticleModel");
|
$p = $apt ? ($apt->priceOverride ?? $apt->priceMultiplier * $cpp) : ($pt->defaultPriceFactor * $cpp);
|
||||||
|
$prices[] = ['title' => $pt->title, 'price' => round($p, 2)]; // Add title and rounded price
|
||||||
}
|
}
|
||||||
|
|
||||||
$priceTypes = WarehouseArticlePriceTypeModel::getAll();
|
usort($prices, fn($x,$y)=>(match($x['title']){'Verkauf'=>1,'Partner'=>2,'Energie Steiermark'=>3,default=>4})<=>(match($y['title']){'Verkauf'=>1,'Partner'=>2,'Energie Steiermark'=>3,default=>4}));
|
||||||
$articlePriceTypes = WarehouseArticlePriceModel::getAll(['articleId' => $articleId]);
|
|
||||||
|
|
||||||
$cheapestSellPrices = [];
|
$a->cheapestSellPrice = json_encode($prices);
|
||||||
// Calculate sell prices for each price type, use default sell multiplier if no specific price is set
|
WarehouseArticleModel::update(get_object_vars($a));
|
||||||
foreach ($priceTypes as $priceType) {
|
|
||||||
$articlePriceType = null;
|
|
||||||
foreach ($articlePriceTypes as $apt) {
|
|
||||||
if ($apt->articlePriceTypeId == $priceType->id) {
|
|
||||||
$articlePriceType = $apt;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$sellPrice = $priceType->defaultPriceFactor * $article->cheapestPurchasePrice;
|
|
||||||
if ($articlePriceType !== null) {
|
|
||||||
$sellPrice = $articlePriceType->priceOverride ?: $articlePriceType->priceMultiplier * $article->cheapestPurchasePrice;
|
|
||||||
}
|
|
||||||
$cheapestSellPrices[$priceType->id] = ['title' => $priceType->title, 'price' => round($sellPrice, 2)];
|
|
||||||
}
|
|
||||||
|
|
||||||
$article->cheapestSellPrice = json_encode($cheapestSellPrices);
|
|
||||||
WarehouseArticleModel::update(get_object_vars($article));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updatePricesAction() {
|
public function updatePricesAction() {
|
||||||
@@ -142,207 +93,4 @@ class WarehouseArticleController extends TTCrud {
|
|||||||
protected function getHistoryAction() {
|
protected function getHistoryAction() {
|
||||||
self::returnJson((new WarehouseHistoryController)->getHistory($this->request->id, $this->mod, $this->columns));
|
self::returnJson((new WarehouseHistoryController)->getHistory($this->request->id, $this->mod, $this->columns));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function importAction() {
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
ini_set('display_errors', 1);
|
|
||||||
// read import.json from directory of this file
|
|
||||||
$json = fopen(dirname(__FILE__) . '/import.json', 'r') or die('Unable to open file!');
|
|
||||||
|
|
||||||
// read file content
|
|
||||||
$data = fread($json, filesize(dirname(__FILE__) . '/import.json'));
|
|
||||||
|
|
||||||
// decode json
|
|
||||||
$data = json_decode($data, true);
|
|
||||||
|
|
||||||
// close file
|
|
||||||
fclose($json);
|
|
||||||
|
|
||||||
// die with data length
|
|
||||||
// loop through data
|
|
||||||
echo 'Importing ' . count($data) . ' items' . PHP_EOL;
|
|
||||||
$count = 0;
|
|
||||||
foreach ($data as $item) {
|
|
||||||
// echo count + 1
|
|
||||||
echo ++$count . PHP_EOL;
|
|
||||||
// Check if Distributor exists
|
|
||||||
$distributor = WarehouseDistributorModel::getAll(['name' => $item['Lieferant']]);
|
|
||||||
if (empty($distributor)) {
|
|
||||||
$distributorId = WarehouseDistributorModel::create(['name' => $item['Lieferant'],
|
|
||||||
'address' => 'Missing',
|
|
||||||
'plz' => 'Missing',
|
|
||||||
'city' => 'Missing',
|
|
||||||
'countryId' => 1,
|
|
||||||
'email' => 'Missing',
|
|
||||||
'phone' => 'Missing',
|
|
||||||
'contactPerson' => 'Missing',]);
|
|
||||||
} else {
|
|
||||||
$distributorId = $distributor[0]->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// only continue if PRODUKT 1.ZEILE and PRODUKT 2.ZEILE and ARTIKEL GRUPPE and VK and EK and Lieferant/ Hersteller Artikelnr: are set
|
|
||||||
if (!isset($item['PRODUKT 1.ZEILE'])) {
|
|
||||||
echo 'Missing data for ' . $item['PRODUKT 1.ZEILE'] . PHP_EOL;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($item['VK'])) {
|
|
||||||
$item['VK'] = 0;
|
|
||||||
$calcSellPriceMultiplier = 0;
|
|
||||||
} else {
|
|
||||||
$item['VK'] = floatval(str_replace(',', '', $item['VK']));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (empty($item['EK'])) {
|
|
||||||
$item['EK'] = 0;
|
|
||||||
$calcSellPriceMultiplier = 0;
|
|
||||||
$purchasePrice = 0;
|
|
||||||
} else {
|
|
||||||
$item['EK'] = floatval(str_replace(',', '', $item['EK']));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($item['VK']) && !empty($item['EK'])) {
|
|
||||||
$calcSellPriceMultiplier = $item['VK'] / $item['EK'];
|
|
||||||
|
|
||||||
// if calcSellPriceMultiplier has more than 2 decimal places assign $calcSellPriceOverride
|
|
||||||
echo strlen(substr(strrchr($calcSellPriceMultiplier, "."), 1)) . PHP_EOL;
|
|
||||||
if (strlen(substr(strrchr($calcSellPriceMultiplier, "."), 1)) > 2) {
|
|
||||||
$calcSellPriceOverride = $item['VK'];
|
|
||||||
}
|
|
||||||
|
|
||||||
$purchasePrice = str_replace(',', '', $item['EK']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($calcSellPriceMultiplier) && !isset($calcSellPriceOverride) || !isset($purchasePrice)) {
|
|
||||||
echo 'Missing data for ' . $item['PRODUKT 1.ZEILE'] . PHP_EOL;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Check if Article exists
|
|
||||||
$article = WarehouseArticleModel::getAll(['title' => ['exact' => $item['PRODUKT 1.ZEILE']]]);
|
|
||||||
if (empty($article)) {
|
|
||||||
$articleCreateData = ['title' => $item['PRODUKT 1.ZEILE'],
|
|
||||||
'description' => $item['PRODUKT 2. ZEILE'],
|
|
||||||
'category' => $item['ARTIKEL GRUPPE'],
|
|
||||||
'cheapestPurchasePrice' => 0,
|
|
||||||
'warningAmount' => 25,
|
|
||||||
'criticalAmount' => 10,
|
|
||||||
'isEShop' => 0,];
|
|
||||||
|
|
||||||
// if calcSellPriceOverride is set, add it to the $articleCreateData array
|
|
||||||
if (isset($calcSellPriceOverride)) {
|
|
||||||
$articleCreateData['sellPriceOverride'] = $calcSellPriceOverride;
|
|
||||||
} else if (isset($calcSellPriceMultiplier)) {
|
|
||||||
$articleCreateData['sellPriceMultiplier'] = $calcSellPriceMultiplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
$articleId = WarehouseArticleModel::create($articleCreateData);
|
|
||||||
} else {
|
|
||||||
echo 'Article already exists with title ' . $item['PRODUKT 1.ZEILE'] . PHP_EOL;
|
|
||||||
$articleId = $article[0]->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if ArticleDistributor exists
|
|
||||||
$articleDistributor = WarehouseArticleDistributorModel::getAll(['articleId' => $articleId,
|
|
||||||
'distributorId' => $distributorId]);
|
|
||||||
if (empty($articleDistributor)) {
|
|
||||||
WarehouseArticleDistributorModel::create(['articleId' => $articleId,
|
|
||||||
'distributorId' => $distributorId,
|
|
||||||
'purchasePrice' => $purchasePrice,
|
|
||||||
'externalArticleNumber' => $item['Lieferant/ Hersteller Artikelnr:'],]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function provArticleNumberImportAction() {
|
|
||||||
// if method is post and file is set read the csv and var dump json and die
|
|
||||||
// if method is get return basic html with a form to upload a file and a submit button
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] == 'POST' && isset($_FILES['file'])) {
|
|
||||||
$file = fopen($_FILES['file']['tmp_name'], 'r');
|
|
||||||
$data = [];
|
|
||||||
// parse csv as object with first row as keys as header and use key => value
|
|
||||||
$firstRow = true;
|
|
||||||
while (($row = fgetcsv($file)) !== false) {
|
|
||||||
if ($firstRow) {
|
|
||||||
$header = $row;
|
|
||||||
$firstRow = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$data[] = array_combine($header, $row);
|
|
||||||
}
|
|
||||||
|
|
||||||
// loop through all the data and if PRODUKT 1.ZEILE is set and articleNumber is not set push the last 4 numbers of "EAN 13 Code" to articleNumber of the found article
|
|
||||||
|
|
||||||
foreach ($data as $item) {
|
|
||||||
if (isset($item['PRODUKT 1.ZEILE'])) {
|
|
||||||
$articles = WarehouseArticleModel::getAll(['title' => $item['PRODUKT 1.ZEILE']]);
|
|
||||||
if (!empty($articles)) {
|
|
||||||
$article = (array) WarehouseArticleModel::get($articles[0]->id);
|
|
||||||
|
|
||||||
$article['articleNumber'] = substr($item['EAN 13 Code'], -4);
|
|
||||||
WarehouseArticleModel::update($article);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose($file);
|
|
||||||
die(json_encode(['success' => true]));
|
|
||||||
}
|
|
||||||
|
|
||||||
$html = '<html><head></head><body><form method="post" enctype="multipart/form-data"><input type="file" name="file"><input type="submit"></form></body></html>';
|
|
||||||
echo $html;
|
|
||||||
die();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function prepareOrderAction() {
|
|
||||||
// inside post json it will look like
|
|
||||||
// [
|
|
||||||
// {
|
|
||||||
// "amount": "5",
|
|
||||||
// "itemId": 441,
|
|
||||||
// "title": "RT-FB-7590AX"
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// "amount": "5",
|
|
||||||
// "itemId": 421,
|
|
||||||
// "title": "RT-FB-7590"
|
|
||||||
// }
|
|
||||||
//]
|
|
||||||
// get the json from the post request
|
|
||||||
// then create a array containing each order we need to make, so search through WarehouseArticleDistributorModel to get the distributorId and purchasePrice (use lowest purchasePrice)
|
|
||||||
// then get the WarehouseDistributorModel and then create a summary of the orders we need to make for each distributor
|
|
||||||
|
|
||||||
$postData = json_decode(file_get_contents('php://input'), true);
|
|
||||||
$orders = [];
|
|
||||||
foreach ($postData as $order) {
|
|
||||||
$articleDistributors = WarehouseArticleDistributorModel::getAll(['articleId' => $order['itemId']]);
|
|
||||||
$cheapestArticleDistributor = $articleDistributors[0];
|
|
||||||
foreach ($articleDistributors as $articleDistributor) {
|
|
||||||
if ($articleDistributor->purchasePrice < $cheapestArticleDistributor->purchasePrice) {
|
|
||||||
$cheapestArticleDistributor = $articleDistributor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$distributor = WarehouseDistributorModel::get($cheapestArticleDistributor->distributorId);
|
|
||||||
|
|
||||||
if (!isset($orders[$distributor->id])) {
|
|
||||||
$orders[$distributor->id] = ['distributor' => array($distributor),
|
|
||||||
'orderAmount' => 0,
|
|
||||||
'orders' => []];
|
|
||||||
}
|
|
||||||
|
|
||||||
$orders[$distributor->id]['orders'][] = ['articleId' => $order['itemId'],
|
|
||||||
'amount' => $order['amount'],
|
|
||||||
'sum' => $order['amount'] * $cheapestArticleDistributor->purchasePrice,
|
|
||||||
'purchasePrice' => $cheapestArticleDistributor->purchasePrice,
|
|
||||||
'externalArticleNumber' => $cheapestArticleDistributor->externalArticleNumber,
|
|
||||||
'title' => $order['title'],];
|
|
||||||
$orders[$distributor->id]['orderAmount'] += $order['amount'] * $cheapestArticleDistributor->purchasePrice;
|
|
||||||
}
|
|
||||||
|
|
||||||
self::returnJson($orders);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
14
application/WarehouseEmailQueue/WarehouseEmailQueue.php
Normal file
14
application/WarehouseEmailQueue/WarehouseEmailQueue.php
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class WarehouseEmailQueue extends TTCrudBaseModel {
|
||||||
|
public int $id;
|
||||||
|
public string $from;
|
||||||
|
public string $fromName;
|
||||||
|
public string $to;
|
||||||
|
public string $toName;
|
||||||
|
public string $subject;
|
||||||
|
public string $body;
|
||||||
|
public int $create;
|
||||||
|
public int $createBy;
|
||||||
|
public ?int $sent;
|
||||||
|
}
|
||||||
@@ -418,7 +418,7 @@ $appendToBody
|
|||||||
)));
|
)));
|
||||||
|
|
||||||
foreach ($requestIds as $requestId) {
|
foreach ($requestIds as $requestId) {
|
||||||
$request = (array) WarehouseOrderRequestModel::get($requestId);
|
$request = (array) WarehouseOrderRequest::get($requestId);
|
||||||
$linkedIds = $request['linkedOrderIds'] ? json_decode($request['linkedOrderIds'], true) : [];
|
$linkedIds = $request['linkedOrderIds'] ? json_decode($request['linkedOrderIds'], true) : [];
|
||||||
|
|
||||||
if ($action === 'add' && !in_array($orderId, $linkedIds)) {
|
if ($action === 'add' && !in_array($orderId, $linkedIds)) {
|
||||||
@@ -432,7 +432,7 @@ $appendToBody
|
|||||||
|
|
||||||
if ($request['linkedOrderIds'] !== $linkedIds) {
|
if ($request['linkedOrderIds'] !== $linkedIds) {
|
||||||
$request['linkedOrderIds'] = $linkedIds;
|
$request['linkedOrderIds'] = $linkedIds;
|
||||||
WarehouseOrderRequestModel::update($request);
|
WarehouseOrderRequest::update($request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ class WarehouseOrderRequestController extends TTCrud {
|
|||||||
if (!$id || $cancel === false) self::returnJson(['error' => 'Ungültige Anfrage']);
|
if (!$id || $cancel === false) self::returnJson(['error' => 'Ungültige Anfrage']);
|
||||||
if (!(WarehouseOrderRequest::get($id))) self::returnJson(['error' => 'Bestellwunsch nicht gefunden']);
|
if (!(WarehouseOrderRequest::get($id))) self::returnJson(['error' => 'Bestellwunsch nicht gefunden']);
|
||||||
|
|
||||||
$currentData = (array) WarehouseOrderRequest::get($id);
|
$currentData = (array)WarehouseOrderRequest::get($id);
|
||||||
WarehouseOrderRequest::update(array_merge($currentData, ['id' => $id, 'cancelled' => $cancel]));
|
WarehouseOrderRequest::update(array_merge($currentData, ['id' => $id, 'cancelled' => $cancel]));
|
||||||
self::returnJson(['success' => true]);
|
self::returnJson(['success' => true]);
|
||||||
}
|
}
|
||||||
@@ -92,66 +92,29 @@ class WarehouseOrderRequestController extends TTCrud {
|
|||||||
if (!$id || $done === false) self::returnJson(['error' => 'Ungültige Anfrage']);
|
if (!$id || $done === false) self::returnJson(['error' => 'Ungültige Anfrage']);
|
||||||
if (!(WarehouseOrderRequest::get($id))) self::returnJson(['error' => 'Bestellwunsch nicht gefunden']);
|
if (!(WarehouseOrderRequest::get($id))) self::returnJson(['error' => 'Bestellwunsch nicht gefunden']);
|
||||||
|
|
||||||
$currentData = (array) WarehouseOrderRequest::get($id);
|
$currentData = (array)WarehouseOrderRequest::get($id);
|
||||||
WarehouseOrderRequest::update(array_merge($currentData, ['id' => $id, 'done' => $done]));
|
WarehouseOrderRequest::update(array_merge($currentData, ['id' => $id, 'done' => $done]));
|
||||||
self::returnJson(['success' => true]);
|
self::returnJson(['success' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getPHPMailer() {
|
|
||||||
$mail = new PHPMailer\PHPMailer\PHPMailer(true);
|
|
||||||
try {
|
|
||||||
// Server settings
|
|
||||||
$mail->isSMTP();
|
|
||||||
$mail->Host = TT_WAREHOUSE_ORDER_SMTP_HOST;
|
|
||||||
$mail->SMTPAuth = true;
|
|
||||||
$mail->Username = TT_WAREHOUSE_ORDER_SMTP_USER;
|
|
||||||
$mail->Password = TT_WAREHOUSE_ORDER_SMTP_PASS;
|
|
||||||
$mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS;
|
|
||||||
$mail->Port = 587;
|
|
||||||
|
|
||||||
return $mail;
|
|
||||||
} catch (Exception $e) {
|
|
||||||
self::returnJson(['error' => 'Mailer Error: ' . $mail->ErrorInfo]);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function afterCreate($orderRequest) {
|
protected function afterCreate($orderRequest) {
|
||||||
try {
|
$rows = '';
|
||||||
$mail = $this->getPHPMailer();
|
|
||||||
|
|
||||||
$mail->setFrom('einkauf@xinon.at', 'XINON Einkauf');
|
|
||||||
$mail->addAddress('einkauf@xinon.at', 'XINON Einkauf');
|
|
||||||
|
|
||||||
$mail->isHTML(true);
|
|
||||||
$mail->Subject = "Neuer Bestellwunsch #" . $orderRequest['id'] . " von " . $this->user->name . ' eingelangt';
|
|
||||||
|
|
||||||
// build html table and fetch articleId if set else use articleId_text if its a text article
|
|
||||||
$html = '<table style="width: 100%; border-collapse: collapse;">';
|
|
||||||
$html .= '<tr><th style="border: 1px solid #000; padding: 8px;">Artikel</th><th style="border: 1px solid #000; padding: 8px;">Menge</th><th style="border: 1px solid #000; padding: 8px;">Zweck</th></tr>';
|
|
||||||
foreach ($orderRequest['positions'] as $position) {
|
foreach ($orderRequest['positions'] as $position) {
|
||||||
$articleId = isset($position['articleId']) ? WarehouseArticleModel::get($position['articleId'])->title : $position['articleId_text'];
|
$rows .= sprintf(
|
||||||
$html .= '<tr>';
|
'<tr><td style="border: 1px solid #000; padding: 8px;">%s</td><td style="border: 1px solid #000; padding: 8px;">%s</td><td style="border: 1px solid #000; padding: 8px;">%s</td></tr>',
|
||||||
$html .= '<td style="border: 1px solid #000; padding: 8px;">' . htmlspecialchars($articleId) . '</td>';
|
htmlspecialchars(isset($position['articleId']) ? WarehouseArticleModel::get($position['articleId'])->title : $position['articleId_text']),
|
||||||
$html .= '<td style="border: 1px solid #000; padding: 8px;">' . htmlspecialchars($position['amount']) . '</td>';
|
htmlspecialchars($position['amount']),
|
||||||
$html .= '<td style="border: 1px solid #000; padding: 8px;">' . htmlspecialchars($position['purpose']) . '</td>';
|
htmlspecialchars($position['purpose'])
|
||||||
$html .= '</tr>';
|
);
|
||||||
}
|
}
|
||||||
$html .= '</table>';
|
$html = '<table style="width: 100%; border-collapse: collapse;"><tr><th style="border: 1px solid #000; padding: 8px;">Artikel</th><th style="border: 1px solid #000; padding: 8px;">Menge</th><th style="border: 1px solid #000; padding: 8px;">Zweck</th></tr>' . $rows . '</table>';
|
||||||
|
|
||||||
// Set the HTML content
|
WarehouseEmailQueue::create([
|
||||||
$mail->Body = "Neuer Bestellwunsch #" . $orderRequest['id'] . " von " . $this->user->name . ' eingelangt<br><br>' .
|
'from' => 'einkauf@xinon.at', 'fromName' => 'XINON Einkauf', 'to' => 'einkauf@xinon.at', 'toName' => 'XINON Einkauf',
|
||||||
'Notiz: ' . htmlspecialchars($orderRequest['note']) . '<br><br>' . $html;
|
'subject' => "Neuer Bestellwunsch #{$orderRequest['id']} eingelangt",
|
||||||
|
'body' => sprintf("Neuer Bestellwunsch #%s von %s eingelangt<br><br>Notiz: %s<br><br>%s", $orderRequest['id'], $this->user->name, htmlspecialchars($orderRequest['note']), $html),
|
||||||
// Send the email
|
'create' => time(), 'createBy' => intval($this->user->id),
|
||||||
if (!$mail->send()) {
|
]);
|
||||||
self::returnJson(['error' => 'Message could not be sent. Mailer Error: ' . $mail->ErrorInfo]);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
self::returnJson(['error' => 'Message could not be sent. Mailer Error: ' . $mail->ErrorInfo]);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function createNewLogAction() {
|
protected function createNewLogAction() {
|
||||||
@@ -162,34 +125,17 @@ class WarehouseOrderRequestController extends TTCrud {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// send email to einkauf@xinon.at
|
if (intval($this->user->id !== 1) && intval($this->user->id !== 7))
|
||||||
// Instantiate PHPMailer
|
WarehouseEmailQueue::create([
|
||||||
$mail = new PHPMailer\PHPMailer\PHPMailer(true);
|
'from' => 'einkauf@xinon.at',
|
||||||
|
'fromName' => 'XINON Einkauf',
|
||||||
try {
|
'to' => 'einkauf@xinon.at',
|
||||||
// Server settings
|
'toName' => 'XINON Einkauf',
|
||||||
$mail->isSMTP();
|
'subject' => 'Neue Nachricht zu Besteallwunsch #' . $postData['orderRequestId'],
|
||||||
$mail->Host = TT_WAREHOUSE_ORDER_SMTP_HOST;
|
'body' => $postData['note'],
|
||||||
$mail->SMTPAuth = true;
|
'create' => date('U'),
|
||||||
$mail->Username = TT_WAREHOUSE_ORDER_SMTP_USER;
|
'createBy' => intval($this->user->id),
|
||||||
$mail->Password = TT_WAREHOUSE_ORDER_SMTP_PASS;
|
]);
|
||||||
$mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS;
|
|
||||||
$mail->Port = 587;
|
|
||||||
|
|
||||||
// Recipients
|
|
||||||
$mail->setFrom('einkauf@xinon.at', 'XINON Einkauf');
|
|
||||||
$mail->addAddress('einkauf@xinon.at', 'XINON Einkauf');
|
|
||||||
|
|
||||||
// Content
|
|
||||||
$mail->isHTML(true);
|
|
||||||
$mail->Subject = "Neue Nachricht zu Besteallwunsch #" . $postData['orderRequestId'];
|
|
||||||
$mail->Body = $postData['note'];
|
|
||||||
|
|
||||||
$mail->send();
|
|
||||||
} catch (Exception $e) {
|
|
||||||
self::returnJson(['error' => 'Message could not be sent. Mailer Error: ' . $mail->ErrorInfo]);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
WarehouseLogModel::create([
|
WarehouseLogModel::create([
|
||||||
"table" => "WarehouseOrderRequest",
|
"table" => "WarehouseOrderRequest",
|
||||||
|
|||||||
34
db/migrations/20250423150000_warehouse_modify_19.php
Normal file
34
db/migrations/20250423150000_warehouse_modify_19.php
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<?php /** @noinspection ALL */
|
||||||
|
declare(strict_types = 1);
|
||||||
|
|
||||||
|
use Phinx\Migration\AbstractMigration;
|
||||||
|
|
||||||
|
final class WarehouseModify19 extends AbstractMigration {
|
||||||
|
public function up(): void {
|
||||||
|
if ($this->getEnvironment() == "thetool") {
|
||||||
|
$WarehouseEmailQueueTable = $this->table("WarehouseEmailQueue");
|
||||||
|
if (!$WarehouseEmailQueueTable->exists()) {
|
||||||
|
$WarehouseEmailQueueTable
|
||||||
|
->addColumn("from", "string", ["limit" => 255])
|
||||||
|
->addColumn("fromName", "string", ["limit" => 255])
|
||||||
|
->addColumn("to", "string", ["limit" => 255])
|
||||||
|
->addColumn("toName", "string", ["limit" => 255])
|
||||||
|
->addColumn("subject", "string", ["limit" => 255])
|
||||||
|
->addColumn("body", "text")
|
||||||
|
->addColumn("create", "integer")
|
||||||
|
->addColumn("createBy", "integer")
|
||||||
|
->addColumn("sent", "integer", ["default" => 0])
|
||||||
|
->create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void {
|
||||||
|
if ($this->getEnvironment() == "thetool") {
|
||||||
|
$WarehouseEmailQueueTable = $this->table("WarehouseEmailQueue");
|
||||||
|
if ($WarehouseEmailQueueTable->exists()) {
|
||||||
|
$WarehouseEmailQueueTable->drop()->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
public/js/pages/WarehouseArticle/WarehouseArticle.css
Normal file
5
public/js/pages/WarehouseArticle/WarehouseArticle.css
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.warehouse-article-prices > div {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, minmax(120px, 1fr)) 72px;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
@@ -1,369 +1,87 @@
|
|||||||
Vue.component('warehouse-distributor-modal', {
|
Vue.component('warehouse-article-prices', {
|
||||||
//language=Vue
|
props: {id: {type: Number, required: true}},
|
||||||
template: `
|
template: `
|
||||||
<tt-modal :show.sync="showModal" title="Lieferanten" :delete="false" :save="false"
|
<div>
|
||||||
@close="$emit('update:show', false)">
|
|
||||||
<div v-for="(row, index) in rows" :key="index" style="display:grid;grid-template-columns: auto auto">
|
<tt-card>
|
||||||
<div style="display:grid; grid-template-columns: auto auto auto">
|
<h4>Artikelpreise</h4>
|
||||||
<tt-autocomplete v-model="row.distributorId"
|
|
||||||
:api-url="window['TT_CONFIG']['BASE_PATH'] + '/WarehouseDistributor/autocomplete'"></tt-autocomplete>
|
<div class="warehouse-article-prices">
|
||||||
<tt-input v-model="row.purchasePrice" placeholder="Price" type="number" sm></tt-input>
|
<div v-for="(price, index) in articlePrices" :key="index" class="article-price">
|
||||||
<tt-input v-model="row.externalArticleNumber" placeholder="External Article Number" sm></tt-input>
|
<div class="article-price-type">
|
||||||
</div>
|
<span v-if="price.isRobot" class="robot-icon"><i class="fa-solid fa-robot"></i></span>
|
||||||
<div class="btn-group mb-4">
|
{{ index }}
|
||||||
<a class="btn btn-sm btn-primary" href="#" @click="saveRow(index)" title="Save Row">
|
|
||||||
<i class="fas fa-check"></i>
|
|
||||||
</a>
|
|
||||||
<a class="btn btn-sm btn-danger" href="#" @click="deleteRow(index)" title="Delete Row">
|
|
||||||
<i class="fas fa-trash"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
|
<tt-input sm v-model="price.priceMultiplier" label="Preisfaktor"/>
|
||||||
|
<tt-input sm v-model="price.priceOverride" label="Preisoverride"/>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<tt-button sm icon="fa-solid fa-trash" class="remove-price" additional-class="btn-danger"></tt-button>
|
||||||
|
<tt-button sm icon="fa-solid fa-save" class="save-price" additional-class="btn-primary"></tt-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a href="#" @click="addRow"><i class="fas fa-plus"></i>Lieferant hinzufügen</a>
|
</div>
|
||||||
|
</div>
|
||||||
|
</tt-card>
|
||||||
|
|
||||||
</tt-modal>
|
</div>`,
|
||||||
`, props: {
|
data: () => ({
|
||||||
show: {type: Boolean, default: false}, id: {type: Number, default: null},
|
window,
|
||||||
}, data() {
|
articlePrices: [],
|
||||||
return {
|
}),
|
||||||
window: window, showModal: false, rows: []
|
async mounted() {
|
||||||
|
const [res1, res2] = await Promise.all([
|
||||||
|
axios.post(window['TT_CONFIG']['BASE_PATH'] + '/WarehouseArticlePrice/get', {filters: {articleId: this.id}}),
|
||||||
|
axios.post(window['TT_CONFIG']['BASE_PATH'] + '/WarehouseArticlePriceType/get')
|
||||||
|
]);
|
||||||
|
const prices = {};
|
||||||
|
res2.data.rows.forEach(t => prices[t.title] = {
|
||||||
|
isRobot: true,
|
||||||
|
articlePriceTypeId: t.id,
|
||||||
|
priceMultiplier: t.defaultPriceFactor,
|
||||||
|
priceOverride: null
|
||||||
|
});
|
||||||
|
res1.data.rows.forEach(p => {
|
||||||
|
const t = res2.data.rows.find(t => t.id === p.articlePriceTypeId);
|
||||||
|
if (t) prices[t.title] = {
|
||||||
|
isRobot: false,
|
||||||
|
articlePriceTypeId: p.articlePriceTypeId,
|
||||||
|
priceMultiplier: p.priceMultiplier || t.defaultPriceFactor,
|
||||||
|
priceOverride: p.priceOverride
|
||||||
};
|
};
|
||||||
}, async mounted() {
|
});
|
||||||
|
this.articlePrices = prices;
|
||||||
}, methods: {
|
|
||||||
async fetchArticleDistributor() {
|
|
||||||
const response = await axios.post(window['TT_CONFIG']['BASE_PATH'] + '/WarehouseArticleDistributor/get', {filters: {articleId: this.id}});
|
|
||||||
|
|
||||||
this.rows = response.data.rows
|
|
||||||
}, addRow() {
|
|
||||||
this.rows.push({distributorId: undefined, price: null, externalArticleNumber: null});
|
|
||||||
}, async saveRow(index) {
|
|
||||||
// post to /WarehouseArticleDistributor/save with rows data and articleId
|
|
||||||
const row = this.rows[index];
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
articleId: this.id, distributorId: row.distributorId, purchasePrice: row.purchasePrice, externalArticleNumber: row.externalArticleNumber
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row.id) {
|
|
||||||
data.id = row.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticleDistributor/${row.id ? 'update' : 'create'}`, data);
|
|
||||||
this.$emit('doUpdate');
|
|
||||||
this.window.notify(response.data.success ? 'success' : 'error',
|
|
||||||
response.data.errors ? Object.values(response.data.errors).join('<br>') : response.data.message);
|
|
||||||
await this.fetchArticleDistributor();
|
|
||||||
}, async deleteRow(index) {
|
|
||||||
const row = this.rows[index];
|
|
||||||
|
|
||||||
if (!row.id) {
|
|
||||||
this.window.notify('error', 'Eintrag hat keine ID');
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = axios.post(window['TT_CONFIG']['BASE_PATH'] + '/WarehouseArticleDistributor/delete', {id: row.id});
|
|
||||||
this.$emit('doUpdate');
|
|
||||||
this.window.notify(response.data.success ? 'success' : 'error',
|
|
||||||
response.data.errors ? Object.values(response.data.errors).join('<br>') : response.data.message);
|
|
||||||
await this.fetchArticleDistributor();
|
|
||||||
}
|
|
||||||
}, watch: {
|
|
||||||
show(newVal) {
|
|
||||||
this.showModal = newVal;
|
|
||||||
if (!newVal) {
|
|
||||||
this.rows = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.rows = [];
|
|
||||||
this.fetchArticleDistributor().then();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Vue.component('warehouse-threshold-modal', {
|
|
||||||
//language=Vue
|
|
||||||
template: `
|
|
||||||
<tt-modal :show.sync="showModal" title="Schwellenwerte" :delete="false" :save="false" @close="$emit('update:show', false)">
|
|
||||||
<div v-for="(row, index) in rows" :key="index" style="display:grid;grid-template-columns: auto auto">
|
|
||||||
<div style="display:grid; grid-template-columns: auto auto auto">
|
|
||||||
<tt-autocomplete v-model="row.locationId"
|
|
||||||
:api-url="window['TT_CONFIG']['BASE_PATH'] + '/WarehouseLocation/autocomplete'"></tt-autocomplete>
|
|
||||||
<tt-input v-model="row.warningAmount" placeholder="Warnmenge" type="number" sm></tt-input>
|
|
||||||
<tt-input v-model="row.criticalAmount" placeholder="Kritische Menge" type="number" sm></tt-input>
|
|
||||||
</div>
|
|
||||||
<div class="btn-group mb-4">
|
|
||||||
<a class="btn btn-sm btn-primary" href="#" @click="saveRow(index)" title="Save Row">
|
|
||||||
<i class="fas fa-check"></i>
|
|
||||||
</a>
|
|
||||||
<a class="btn btn-sm btn-danger" href="#" @click="deleteRow(index)" title="Delete Row">
|
|
||||||
<i class="fas fa-trash"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a href="#" @click="addRow"><i class="fas fa-plus"></i>Schwellenwert hinzufügen</a>
|
|
||||||
|
|
||||||
</tt-modal>
|
|
||||||
`, props: {
|
|
||||||
show: {type: Boolean, default: false}, id: {type: Number, default: null},
|
|
||||||
}, data() {
|
|
||||||
return {
|
|
||||||
window: window, showModal: false, rows: []
|
|
||||||
};
|
|
||||||
}, async mounted() {
|
|
||||||
|
|
||||||
}, methods: {
|
|
||||||
async fetchLocationThreshold() {
|
|
||||||
const response = await axios.post(window['TT_CONFIG']['BASE_PATH'] + '/WarehouseLocationThresholdOverride/get', {filters: {articleId: this.id}});
|
|
||||||
|
|
||||||
this.rows = response.data.rows
|
|
||||||
}, addRow() {
|
|
||||||
this.rows.push({distributor: null, price: null, externalArticleNumber: null});
|
|
||||||
}, async saveRow(index) {
|
|
||||||
// post to /WarehouseArticleDistributor/save with rows data and articleId
|
|
||||||
const row = this.rows[index];
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
articleId: this.id, locationId: row.locationId, warningAmount: row.warningAmount, criticalAmount: row.criticalAmount
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row.id) {
|
|
||||||
data.id = row.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseLocationThresholdOverride/${row.id ? 'update' : 'create'}`, data);
|
|
||||||
this.$emit('doUpdate');
|
|
||||||
this.window.notify(response.data.success ? 'success' : 'error',
|
|
||||||
response.data.errors ? Object.values(response.data.errors).join('<br>') : response.data.message);
|
|
||||||
await this.fetchLocationThreshold()
|
|
||||||
}, async deleteRow(index) {
|
|
||||||
const row = this.rows[index];
|
|
||||||
|
|
||||||
if (!row.id) {
|
|
||||||
this.window.notify('error', 'Eintrag hat keine ID');
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = axios.post(window['TT_CONFIG']['BASE_PATH'] + '/WarehouseLocationThresholdOverride/delete', {id: row.id});
|
|
||||||
this.$emit('doUpdate');
|
|
||||||
this.window.notify(response.data.success ? 'success' : 'error',
|
|
||||||
response.data.errors ? Object.values(response.data.errors).join('<br>') : response.data.message);
|
|
||||||
await this.fetchLocationThreshold();
|
|
||||||
}
|
|
||||||
}, watch: {
|
|
||||||
show(newVal) {
|
|
||||||
this.showModal = newVal;
|
|
||||||
if (!newVal) {
|
|
||||||
this.rows = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.rows = [];
|
|
||||||
this.fetchLocationThreshold().then();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Vue.component('warehouse-article-price-modal', {
|
|
||||||
//language=Vue
|
|
||||||
template: `
|
|
||||||
<tt-modal :show.sync="showModal" title="Artikel-Preise" :delete="false" :save="false" @close="$emit('update:show', false)">
|
|
||||||
<div v-for="(row, index) in rows" :key="index" style="display:grid;grid-template-columns: auto auto">
|
|
||||||
<div style="display:grid; grid-template-columns: auto auto auto">
|
|
||||||
<tt-autocomplete v-model="row.articlePriceTypeId"
|
|
||||||
:api-url="window['TT_CONFIG']['BASE_PATH'] + '/WarehouseArticlePriceType/autocomplete'"></tt-autocomplete>
|
|
||||||
<tt-input v-model="row.priceMultiplier" placeholder="Preis Multiplikator" type="number" sm></tt-input>
|
|
||||||
<tt-input v-model="row.priceOverride" placeholder="Preis" type="number" sm></tt-input>
|
|
||||||
</div>
|
|
||||||
<div class="btn-group mb-4">
|
|
||||||
<a class="btn btn-sm btn-primary" href="#" @click="saveRow(index)" title="Save Row">
|
|
||||||
<i class="fas fa-check"></i>
|
|
||||||
</a>
|
|
||||||
<a class="btn btn-sm btn-danger" href="#" @click="deleteRow(index)" title="Delete Row">
|
|
||||||
<i class="fas fa-trash"></i>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<a href="#" @click="addRow"><i class="fas fa-plus"></i>Preis hinzufügen</a>
|
|
||||||
|
|
||||||
</tt-modal>
|
|
||||||
`, props: {
|
|
||||||
show: {type: Boolean, default: false}, id: {type: Number, default: null},
|
|
||||||
}, data() {
|
|
||||||
return {
|
|
||||||
window: window, showModal: false, rows: []
|
|
||||||
};
|
|
||||||
}, async mounted() {
|
|
||||||
|
|
||||||
}, methods: {
|
|
||||||
async fetchLocationThreshold() {
|
|
||||||
const response = await axios.post(window['TT_CONFIG']['BASE_PATH'] + '/WarehouseArticlePrice/get', {filters: {articleId: this.id}});
|
|
||||||
|
|
||||||
this.rows = response.data.rows
|
|
||||||
}, addRow() {
|
|
||||||
this.rows.push({articlePriceTypeId: null, priceMultiplier: null, priceOverride: null});
|
|
||||||
}, async saveRow(index) {
|
|
||||||
// post to /WarehouseArticleDistributor/save with rows data and articleId
|
|
||||||
const row = this.rows[index];
|
|
||||||
|
|
||||||
const data = {
|
|
||||||
articleId: this.id, articlePriceTypeId: row.articlePriceTypeId, priceMultiplier: row.priceMultiplier, priceOverride: row.priceOverride
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row.id) {
|
|
||||||
data.id = row.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticlePrice/${row.id ? 'update' : 'create'}`, data);
|
|
||||||
this.$emit('doUpdate');
|
|
||||||
this.window.notify(response.data.success ? 'success' : 'error',
|
|
||||||
response.data.errors ? Object.values(response.data.errors).join('<br>') : response.data.message);
|
|
||||||
await this.fetchLocationThreshold()
|
|
||||||
}, async deleteRow(index) {
|
|
||||||
const row = this.rows[index];
|
|
||||||
|
|
||||||
if (!row.id) {
|
|
||||||
this.window.notify('error', 'Eintrag hat keine ID');
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = axios.post(window['TT_CONFIG']['BASE_PATH'] + '/WarehouseArticlePrice/delete', {id: row.id});
|
|
||||||
this.$emit('doUpdate');
|
|
||||||
this.window.notify(response.data.success ? 'success' : 'error',
|
|
||||||
response.data.errors ? Object.values(response.data.errors).join('<br>') : response.data.message);
|
|
||||||
await this.fetchLocationThreshold();
|
|
||||||
}
|
|
||||||
}, watch: {
|
|
||||||
show(newVal) {
|
|
||||||
this.showModal = newVal;
|
|
||||||
if (!newVal) {
|
|
||||||
this.rows = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.rows = [];
|
|
||||||
this.fetchLocationThreshold().then();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// noinspection EqualityComparisonWithCoercionJS
|
|
||||||
Vue.component('warehouse-article', {
|
Vue.component('warehouse-article', {
|
||||||
//language=Vue
|
//language=Vue
|
||||||
template: `
|
template: `
|
||||||
<tt-card>
|
<tt-card>
|
||||||
|
|
||||||
<tt-table-crud ref="table"
|
<tt-table-crud ref="table" @openHistory="historyModal = true; historyModalId = $event.id">
|
||||||
@openHistory="historyModal = true; historyModalId = $event.id"
|
|
||||||
@editDistributorEntries="distributorModal = true; distributorModalId = $event.id"
|
|
||||||
@editPricesEntries="priceModal = true; priceModalId = $event.id"
|
|
||||||
@editThresholdEntries="thresholdModal = true; thresholdModalId = $event.id"
|
|
||||||
@addToCart="addShoppingCartModal = true; addShoppingCartModalId = $event.id"
|
|
||||||
>
|
|
||||||
|
|
||||||
<template v-slot:cheapestsellprice="{ row }">
|
<template v-slot:cheapestsellprice="{ row }">
|
||||||
<template v-for="price in JSON.parse(row.cheapestSellPrice)">
|
<template v-for="price in JSON.parse(row.cheapestSellPrice)">
|
||||||
<span v-if="price && window.TT_CONFIG['WAREHOUSE_ADMIN'] == true">{{price.title}}: {{(price.price)}} €<br></span>
|
<span v-if="price && window.TT_CONFIG['WAREHOUSE_ADMIN'] == true">{{price.title}}: <span
|
||||||
<span v-if="price && price.title === 'Verkauf'">{{(price.price)}} €</span>
|
style="white-space:nowrap;"> {{(price.price)}} €</span><br></span>
|
||||||
|
<span v-else-if="price && price.title === 'Verkauf'">{{(price.price)}} €</span>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-slot:cheapestPurchasePrice="{ row }">
|
<template v-slot:modal-prepend="{ crudModalData }">
|
||||||
<span>{{(row.cheapestPurchasePrice * row.sellPriceMultiplier).toFixed(2)}} €</span>
|
<warehouse-article-prices v-if="crudModalData.id" :id="crudModalData.id"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</tt-table-crud>
|
</tt-table-crud>
|
||||||
|
|
||||||
<tt-expandable-shopping-cart v-if="window.TT_CONFIG['WAREHOUSE_ADMIN'] === true" :cart-items="shoppingCart" @submitOrder="prepareOrder"/>
|
|
||||||
<tt-modal :show.sync="addShoppingCartModal" title="Artikel zur Bestellung hinzufügen" :delete="false" @submit="addToShoppingCart"
|
|
||||||
@close="addShoppingCartModal = false">
|
|
||||||
<tt-input v-model="addShoppingCartModalCount" placeholder="Menge" type="number" sm></tt-input>
|
|
||||||
</tt-modal>
|
|
||||||
|
|
||||||
<tt-modal :show.sync="confirmOrderModal" title="Bestellung bestätigen" :delete="false"
|
|
||||||
save-text="Bestätigen" @submit="createOrder"
|
|
||||||
@close="confirmOrderModal = false; confirmOrderModalData = null">
|
|
||||||
<span>
|
|
||||||
Es werden Bestellungen an folgende Lieferanten gesendet:
|
|
||||||
</span>
|
|
||||||
<div v-for="(order, index) in confirmOrderModalData" :key="index">
|
|
||||||
<h4>{{order.distributor[0].name}} - {{order.orderAmount}} €
|
|
||||||
</h4>
|
|
||||||
<table class="table table-bordered">
|
|
||||||
<tr>
|
|
||||||
<th>Artikel</th>
|
|
||||||
<th>Menge</th>
|
|
||||||
<th>Preis</th>
|
|
||||||
<th>Summe</th>
|
|
||||||
</tr>
|
|
||||||
<tr v-for="(item, index) in order.orders" :key="index + item.id">
|
|
||||||
<td>{{item.title}}</td>
|
|
||||||
<td>{{item.amount}}</td>
|
|
||||||
<td>{{item.purchasePrice}} €</td>
|
|
||||||
<td>{{item.sum.toFixed(2)}} €</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</tt-modal>
|
|
||||||
|
|
||||||
<warehouse-distributor-modal :show.sync="distributorModal" :id="distributorModalId" @doUpdate="refreshTable"/>
|
|
||||||
<warehouse-threshold-modal :show.sync="thresholdModal" :id="thresholdModalId" @doUpdate="refreshTable"/>
|
|
||||||
<warehouse-article-price-modal :show.sync="priceModal" :id="priceModalId" @doUpdate="refreshTable"/>
|
|
||||||
<warehouse-history-modal :show.sync="historyModal" :id="historyModalId"/>
|
<warehouse-history-modal :show.sync="historyModal" :id="historyModalId"/>
|
||||||
</tt-card>
|
</tt-card>
|
||||||
`, data() {
|
`, data: () => ({
|
||||||
return {
|
window,
|
||||||
window: window,
|
|
||||||
historyModal: false,
|
historyModal: false,
|
||||||
historyModalId: null,
|
historyModalId: null,
|
||||||
distributorModal: false,
|
}),
|
||||||
distributorModalId: null,
|
|
||||||
thresholdModal: false,
|
|
||||||
thresholdModalId: null,
|
|
||||||
priceModal: false,
|
|
||||||
priceModalId: null,
|
|
||||||
shoppingCart: [],
|
|
||||||
addShoppingCartModal: false,
|
|
||||||
addShoppingCartModalId: null,
|
|
||||||
addShoppingCartModalCount: '',
|
|
||||||
confirmOrderModal: false,
|
|
||||||
confirmOrderModalData: null,
|
|
||||||
|
|
||||||
}
|
|
||||||
}, methods: {
|
|
||||||
refreshTable() {
|
|
||||||
this.$refs.table.$refs.table.refreshTable();
|
|
||||||
}, async addToShoppingCart() {
|
|
||||||
if (this.addShoppingCartModalCount < 1) { // Check if amount is set
|
|
||||||
window.notify('error', 'Bitte geben Sie eine Menge ein.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.shoppingCart.some(item => item.itemId === this.addShoppingCartModalId)) { // Check if same article is already in cart
|
|
||||||
window.notify('error', 'Artikel bereits im Warenkorb.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await axios.get(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticle/getById?id=${this.addShoppingCartModalId}`);
|
|
||||||
this.shoppingCart.push({amount: parseInt(this.addShoppingCartModalCount), itemId: this.addShoppingCartModalId, title: response.data.title});
|
|
||||||
this.addShoppingCartModal = false;
|
|
||||||
this.addShoppingCartModalId = null;
|
|
||||||
this.addShoppingCartModalCount = '';
|
|
||||||
window.notify('success', 'Artikel erfolgreich hinzugefügt.');
|
|
||||||
}, async prepareOrder() {
|
|
||||||
const response = await axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseArticle/prepareOrder`, this.shoppingCart);
|
|
||||||
this.confirmOrderModal = true;
|
|
||||||
this.confirmOrderModalData = response.data;
|
|
||||||
},
|
|
||||||
async createOrder() {
|
|
||||||
const response = await axios.post(`${window['TT_CONFIG']['BASE_PATH']}/WarehouseOrder/createOrder`, this.confirmOrderModalData);
|
|
||||||
this.confirmOrderModal = false;
|
|
||||||
this.confirmOrderModalData = null;
|
|
||||||
this.shoppingCart = [];
|
|
||||||
window.notify(response.data.success ? 'success' : 'error', response.data.message);
|
|
||||||
setTimeout(() => {
|
|
||||||
window.location.href = `${window['TT_CONFIG']['BASE_PATH']}/WarehouseOrder`;
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
mounted() {
|
||||||
const table = this.$refs.table?.$refs?.table;
|
const table = this.$refs.table?.$refs?.table;
|
||||||
if (!table) return;
|
if (!table) return;
|
||||||
@@ -372,7 +90,7 @@ Vue.component('warehouse-article', {
|
|||||||
const currentFilterId = table.filters?.id;
|
const currentFilterId = table.filters?.id;
|
||||||
|
|
||||||
if ((showId && currentFilterId !== showId) || (!showId && currentFilterId)) {
|
if ((showId && currentFilterId !== showId) || (!showId && currentFilterId)) {
|
||||||
table.filters = showId ? { id: showId } : {};
|
table.filters = showId ? {id: showId} : {};
|
||||||
table.refreshTable();
|
table.refreshTable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -297,7 +297,11 @@ Vue.component('warehouse-shipping-note-see-through', {
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div v-if="activeTab === 'Editieren'" style="flex: 1; overflow: auto;" class="see-through-test-modal">
|
<div v-if="activeTab === 'Editieren'" style="flex: 1; overflow: auto;" class="see-through-test-modal">
|
||||||
<warehouse-shipping-note-modal v-if="currentRow" :id="currentRow.id" @close="fetchData" ref="modal"/>
|
<warehouse-shipping-note-modal v-if="currentRow" :id="currentRow.id" @close="fetchData" ref="modal">
|
||||||
|
<template v-slot:footer-prepend v-if="wantedState === 'new'">
|
||||||
|
<button class="btn btn-warning" @click="saveAndSetToProgress">Speichern und in Bearbeitung setzen</button>
|
||||||
|
</template>
|
||||||
|
</warehouse-shipping-note-modal>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="activeTab === 'Logs'" style="flex: 1; overflow: auto;" class="see-through-test-modal">
|
<div v-if="activeTab === 'Logs'" style="flex: 1; overflow: auto;" class="see-through-test-modal">
|
||||||
<warehouse-shipping-note-logs :shipping-note-id="currentRow.id" :key="'logs' + logModalKey"/>
|
<warehouse-shipping-note-logs :shipping-note-id="currentRow.id" :key="'logs' + logModalKey"/>
|
||||||
@@ -368,6 +372,11 @@ Vue.component('warehouse-shipping-note-see-through', {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
},
|
||||||
|
async saveAndSetToProgress() {
|
||||||
|
await this.$refs.modal.submit('in_progress');
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 50));
|
||||||
|
await this.fetchData();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -381,7 +390,7 @@ Vue.component('warehouse-shipping-note', {
|
|||||||
template: `
|
template: `
|
||||||
<tt-card>
|
<tt-card>
|
||||||
<warehouse-shipping-note-see-through
|
<warehouse-shipping-note-see-through
|
||||||
@close="shippingNoteSeeThrough = false"
|
@close="shippingNoteSeeThrough = false;$refs.table.$refs.table.refreshTable()"
|
||||||
v-if="shippingNoteSeeThrough !== false"
|
v-if="shippingNoteSeeThrough !== false"
|
||||||
:wanted-state="shippingNoteSeeThrough"
|
:wanted-state="shippingNoteSeeThrough"
|
||||||
@status_to_progress="changeStatus($event.id, 'in_progress')"
|
@status_to_progress="changeStatus($event.id, 'in_progress')"
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ Vue.component('warehouse-shipping-note-modal', {
|
|||||||
<template v-if="window.TT_CONFIG['WAREHOUSE_ADMIN'] == true">
|
<template v-if="window.TT_CONFIG['WAREHOUSE_ADMIN'] == true">
|
||||||
<hr>
|
<hr>
|
||||||
<tt-autocomplete v-model="shippingNote.billingAddressId" :api-url="billAddrAutoCompleteUrl" label="Rechnungsadresse" sm row/>
|
<tt-autocomplete v-model="shippingNote.billingAddressId" :api-url="billAddrAutoCompleteUrl" label="Rechnungsadresse" sm row/>
|
||||||
<tt-autocomplete v-model="shippingNote.type" :items="availableTypes" label="Typ" sm row return-text/>
|
<tt-autocomplete v-model="shippingNote.type" :items="availableTypes" label="Typ" sm row return-text case-insensitive/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<tt-textarea v-model="shippingNote.note" label="Art der Arbeit" sm row/>
|
<tt-textarea v-model="shippingNote.note" label="Art der Arbeit" sm row/>
|
||||||
@@ -138,9 +138,13 @@ Vue.component('warehouse-shipping-note-modal', {
|
|||||||
<div v-else class="text-danger text-center font-weight-bolder font-16">Bitte Lieferadresse eingeben oder nach Kunden suchen</div>
|
<div v-else class="text-danger text-center font-weight-bolder font-16">Bitte Lieferadresse eingeben oder nach Kunden suchen</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<template v-slot:footer-prepend v-if="id !== 'create'">
|
<template v-slot:footer-prepend v-if="id !== 'create' && !$slots['footer-prepend']">
|
||||||
<button class="btn btn-info" @click="$emit('open-signing-modal', id)">Unterschreiben</button>
|
<button class="btn btn-info" @click="$emit('open-signing-modal', id)">Unterschreiben</button>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-slot:footer-prepend v-else>
|
||||||
|
<slot name="footer-prepend"/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
</tt-modal>
|
</tt-modal>
|
||||||
`,
|
`,
|
||||||
@@ -177,7 +181,7 @@ Vue.component('warehouse-shipping-note-modal', {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async submit() {
|
async submit(newStatus = null) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
if (!this.shippingNote.positions.length && !this.shippingNote.hoursEntries.length) {
|
if (!this.shippingNote.positions.length && !this.shippingNote.hoursEntries.length) {
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
@@ -188,6 +192,10 @@ Vue.component('warehouse-shipping-note-modal', {
|
|||||||
this.shippingNote.type = this.availableTypes.find(t => t.name === this.shippingNote.type).value;
|
this.shippingNote.type = this.availableTypes.find(t => t.name === this.shippingNote.type).value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (newStatus !== null) {
|
||||||
|
this.shippingNote.status = newStatus;
|
||||||
|
}
|
||||||
|
|
||||||
const response = await axios.post(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseShippingNote/${this.id === 'create' ? 'create' : 'update'}`, this.shippingNote);
|
const response = await axios.post(`${window.TT_CONFIG["BASE_PATH"]}/WarehouseShippingNote/${this.id === 'create' ? 'create' : 'update'}`, this.shippingNote);
|
||||||
|
|
||||||
if ((this.id !== 'create' && response.data.success === true) || response.data.success === true) this.$emit('close');
|
if ((this.id !== 'create' && response.data.success === true) || response.data.success === true) this.$emit('close');
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ Vue.component('tt-autocomplete', {
|
|||||||
row: {type: Boolean, default: false},
|
row: {type: Boolean, default: false},
|
||||||
returnText: {type: Boolean, default: false},
|
returnText: {type: Boolean, default: false},
|
||||||
emitDisplayValue: {type: Boolean, default: false},
|
emitDisplayValue: {type: Boolean, default: false},
|
||||||
|
caseInsensitive: {type: Boolean, default: false},
|
||||||
}, data() {
|
}, data() {
|
||||||
return {
|
return {
|
||||||
window,
|
window,
|
||||||
@@ -125,7 +126,9 @@ Vue.component('tt-autocomplete', {
|
|||||||
const response = await axios.get(`${this.apiUrl}${this.apiUrl.includes('?') ? '&' : '?'}autocomplete=1&searchedID=${this.value}`);
|
const response = await axios.get(`${this.apiUrl}${this.apiUrl.includes('?') ? '&' : '?'}autocomplete=1&searchedID=${this.value}`);
|
||||||
this.displayValue = response.data[0].text;
|
this.displayValue = response.data[0].text;
|
||||||
} else if (this.value) {
|
} else if (this.value) {
|
||||||
const selectedItem = this.items.find(item => item.value === this.value);
|
const selectedItem = this.caseInsensitive ?
|
||||||
|
this.items.find(item => item.value.toLowerCase() === this.value.toLowerCase()) : this.items.find(item => item.value === this.value)
|
||||||
|
;
|
||||||
this.displayValue = selectedItem ? selectedItem.text : this.displayValue;
|
this.displayValue = selectedItem ? selectedItem.text : this.displayValue;
|
||||||
} else {
|
} else {
|
||||||
if (this.returnText === false && !(typeof this.value === 'undefined' || this.value === '')) this.$emit('input', '');
|
if (this.returnText === false && !(typeof this.value === 'undefined' || this.value === '')) this.$emit('input', '');
|
||||||
|
|||||||
@@ -74,6 +74,8 @@ Vue.component('tt-table-crud', {
|
|||||||
<!-- @formatter:on -->
|
<!-- @formatter:on -->
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<slot name="modal-prepend" :crudModalData="crudModalData"></slot>
|
||||||
|
|
||||||
</tt-modal>
|
</tt-modal>
|
||||||
</div>
|
</div>
|
||||||
`, props: {
|
`, props: {
|
||||||
|
|||||||
53
scripts/warehouse-email-queue-process.php
Normal file
53
scripts/warehouse-email-queue-process.php
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
/** @noinspection PhpUndefinedClassInspection */
|
||||||
|
/** @noinspection PhpUndefinedNamespaceInspection */
|
||||||
|
/** @noinspection PhpVoidFunctionResultUsedInspection */
|
||||||
|
require("../config/config.php");
|
||||||
|
|
||||||
|
define('FRONKDB_SQLDEBUG', false);
|
||||||
|
error_reporting(E_ALL & ~(E_NOTICE | E_STRICT | E_DEPRECATED));
|
||||||
|
|
||||||
|
require_once(LIBDIR . "/mvcfronk/mfRouter/mfRouter.php");
|
||||||
|
require_once(LIBDIR . "/mvcfronk/mfBase/mfBaseModel.php");
|
||||||
|
require_once(LIBDIR . "/mvcfronk/mfBase/mfBaseController.php");
|
||||||
|
|
||||||
|
define("INTERNAL_USER_ID", 1);
|
||||||
|
$me = new User(1);
|
||||||
|
|
||||||
|
foreach (WarehouseEmailQueue::getAll(["sent" => 0]) as $email) {
|
||||||
|
$date = date('Y-m-d H:i:s');
|
||||||
|
$mail = new PHPMailer\PHPMailer\PHPMailer(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Server settings
|
||||||
|
$mail->isSMTP();
|
||||||
|
$mail->Host = TT_WAREHOUSE_ORDER_SMTP_HOST;
|
||||||
|
$mail->SMTPAuth = true;
|
||||||
|
$mail->Username = TT_WAREHOUSE_ORDER_SMTP_USER;
|
||||||
|
$mail->Password = TT_WAREHOUSE_ORDER_SMTP_PASS;
|
||||||
|
$mail->SMTPSecure = PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_STARTTLS;
|
||||||
|
$mail->Port = 587;
|
||||||
|
|
||||||
|
// Recipients
|
||||||
|
$mail->setFrom($email->from, $email->fromName);
|
||||||
|
$mail->addAddress($email->to, $email->toName);
|
||||||
|
|
||||||
|
// Content
|
||||||
|
$mail->isHTML(true);
|
||||||
|
$mail->Subject = $email->subject;
|
||||||
|
$mail->Body = $email->body;
|
||||||
|
|
||||||
|
if ($mail->send()) {
|
||||||
|
$email = (array)$email;
|
||||||
|
$email['sent'] = date('U');
|
||||||
|
WarehouseEmailQueue::update($email);
|
||||||
|
echo "[WarehouseEmailQueue] ($date) Message sent to {$email['to']} <br>";
|
||||||
|
} else {
|
||||||
|
echo "[WarehouseEmailQueue] ($date) Message could not be sent to {$email['to']} <br>";
|
||||||
|
echo "[WarehouseEmailQueue] Mailer Error: {$mail->ErrorInfo} <br>";
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "[WarehouseEmailQueue] ($date) Message could not be sent to {$email['to']} <br>";
|
||||||
|
echo "[WarehouseEmailQueue] Mailer Error: {$mail->ErrorInfo} <br>";
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user