582 lines
23 KiB
PHP
582 lines
23 KiB
PHP
<?php
|
|
|
|
require_once APPDIR . 'MobileApp/Shared/MobileAppBaseHandler.php';
|
|
|
|
class MovementHandler extends MobileAppBaseHandler {
|
|
|
|
protected $requiredPermission = 'WarehouseUser';
|
|
|
|
public function getLocationsAction() {
|
|
$allLocations = WarehouseLocationModel::getAll();
|
|
$locations = [];
|
|
|
|
foreach ($allLocations as $location) {
|
|
$title = strtolower($location->title);
|
|
if ($title === 'k1 fladnitz 150' || $title === 'aussenlager-extern') {
|
|
$locations[] = [
|
|
'id' => $location->id,
|
|
'title' => $location->title,
|
|
];
|
|
}
|
|
}
|
|
|
|
self::returnJson(['success' => true, 'locations' => $locations]);
|
|
}
|
|
|
|
public function getArticleAction() {
|
|
$code = $this->request->code;
|
|
|
|
if (!$code) {
|
|
self::returnJson(['success' => false, 'message' => 'Kein Code angegeben']);
|
|
return;
|
|
}
|
|
|
|
$articleId = null;
|
|
|
|
// Check for QR code format WA:ID: or WH:ID:
|
|
if (preg_match('/^(?:WA|WH):(\d+):/', $code, $matches)) {
|
|
$articleId = intval($matches[1]);
|
|
} else {
|
|
// Try to find by article number
|
|
$article = WarehouseArticleModel::getFirst(['articleNumber' => $code]);
|
|
if ($article) {
|
|
$articleId = $article->id;
|
|
}
|
|
}
|
|
|
|
if (!$articleId) {
|
|
self::returnJson(['success' => false, 'message' => 'Artikel nicht gefunden']);
|
|
return;
|
|
}
|
|
|
|
$article = WarehouseArticleModel::get($articleId);
|
|
if (!$article) {
|
|
self::returnJson(['success' => false, 'message' => 'Artikel nicht gefunden']);
|
|
return;
|
|
}
|
|
|
|
$category = WarehouseCategory::get($article->category_id);
|
|
|
|
self::returnJson([
|
|
'success' => true,
|
|
'article' => [
|
|
'id' => $article->id,
|
|
'articleNumber' => $article->articleNumber,
|
|
'title' => $article->title,
|
|
'description' => $article->description ?? '',
|
|
'unit' => $article->unit ?? 'Stk.',
|
|
'categoryName' => $category ? $category->name : '',
|
|
]
|
|
]);
|
|
}
|
|
|
|
public function searchArticlesAction() {
|
|
$query = $this->request->query ?? '';
|
|
|
|
$db = $this->db();
|
|
$conditions = ["(isEndOfLife IS NULL OR isEndOfLife = 0)"];
|
|
|
|
if ($query && strlen($query) >= 2) {
|
|
$escapedQuery = $db->escape($query);
|
|
$conditions[] = "(articleNumber LIKE '%{$escapedQuery}%' OR title LIKE '%{$escapedQuery}%' OR description LIKE '%{$escapedQuery}%')";
|
|
} else {
|
|
self::returnJson(['success' => true, 'articles' => []]);
|
|
return;
|
|
}
|
|
|
|
$whereClause = implode(' AND ', $conditions);
|
|
$result = $db->query("SELECT id, articleNumber, title, unit, category_id
|
|
FROM WarehouseArticle
|
|
WHERE {$whereClause}
|
|
ORDER BY title ASC
|
|
LIMIT 50");
|
|
|
|
$articles = [];
|
|
while ($row = $result->fetch_assoc()) {
|
|
$articles[] = [
|
|
'id' => intval($row['id']),
|
|
'articleNumber' => $row['articleNumber'],
|
|
'title' => $row['title'],
|
|
'unit' => $row['unit'] ?? 'Stk.',
|
|
];
|
|
}
|
|
|
|
self::returnJson(['success' => true, 'articles' => $articles]);
|
|
}
|
|
|
|
public function getReasonCategoriesAction() {
|
|
$type = $this->request->type ?? null;
|
|
|
|
$categories = WarehouseMovementModel::getReasonCategories($type);
|
|
|
|
if ($type && is_array($categories)) {
|
|
$items = [];
|
|
foreach ($categories as $key => $label) {
|
|
$items[] = ['value' => $key, 'text' => $label];
|
|
}
|
|
self::returnJson(['success' => true, 'categories' => $items]);
|
|
} else {
|
|
self::returnJson(['success' => true, 'categories' => $categories]);
|
|
}
|
|
}
|
|
|
|
public function getCurrentStockAction() {
|
|
$articleId = intval($this->request->articleId ?? 0);
|
|
$locationId = intval($this->request->locationId ?? 0);
|
|
|
|
if (!$articleId || !$locationId) {
|
|
self::returnJson(['success' => true, 'currentStock' => 0]);
|
|
return;
|
|
}
|
|
|
|
$existingItems = WarehouseItemModel::getAll([
|
|
'articleId' => $articleId,
|
|
'warehouseLocationId' => $locationId
|
|
]);
|
|
|
|
$currentStock = count($existingItems) > 0 ? floatval($existingItems[0]->quantity) : 0;
|
|
|
|
self::returnJson(['success' => true, 'currentStock' => $currentStock]);
|
|
}
|
|
|
|
public function submitMovementAction() {
|
|
$postData = $this->getPostData();
|
|
|
|
$movementType = $postData['movementType'] ?? '';
|
|
$articleId = intval($postData['articleId'] ?? 0);
|
|
$locationId = intval($postData['locationId'] ?? 0);
|
|
$quantity = floatval($postData['quantity'] ?? 0);
|
|
$reasonCategory = $postData['reasonCategory'] ?? '';
|
|
$note = $postData['note'] ?? null;
|
|
|
|
// Validate required fields
|
|
if (!in_array($movementType, ['IN', 'OUT', 'ADJUSTMENT'])) {
|
|
self::returnJson(['success' => false, 'message' => 'Ungültiger Bewegungstyp']);
|
|
return;
|
|
}
|
|
|
|
if ($articleId <= 0) {
|
|
self::returnJson(['success' => false, 'message' => 'Kein Artikel ausgewählt']);
|
|
return;
|
|
}
|
|
|
|
if ($locationId <= 0) {
|
|
self::returnJson(['success' => false, 'message' => 'Kein Lagerort ausgewählt']);
|
|
return;
|
|
}
|
|
|
|
if ($quantity <= 0) {
|
|
self::returnJson(['success' => false, 'message' => 'Menge muss größer als 0 sein']);
|
|
return;
|
|
}
|
|
|
|
if (empty($reasonCategory)) {
|
|
self::returnJson(['success' => false, 'message' => 'Bitte Grund auswählen']);
|
|
return;
|
|
}
|
|
|
|
// Get article info
|
|
$article = WarehouseArticleModel::get($articleId);
|
|
if (!$article) {
|
|
self::returnJson(['success' => false, 'message' => 'Artikel nicht gefunden']);
|
|
return;
|
|
}
|
|
|
|
$db = $this->db();
|
|
|
|
// Find or create WarehouseItem for this article at this location
|
|
$existingItems = WarehouseItemModel::getAll([
|
|
'articleId' => $articleId,
|
|
'warehouseLocationId' => $locationId
|
|
]);
|
|
|
|
$warehouseItem = count($existingItems) > 0 ? $existingItems[0] : null;
|
|
$currentQty = $warehouseItem ? floatval($warehouseItem->quantity) : 0;
|
|
|
|
// Calculate new quantity based on movement type
|
|
// Note: Negative stock is allowed (items can be taken out even if stock is 0)
|
|
switch ($movementType) {
|
|
case 'IN':
|
|
$newQty = $currentQty + $quantity;
|
|
break;
|
|
case 'OUT':
|
|
$newQty = $currentQty - $quantity;
|
|
// Negative stock is allowed - no validation needed
|
|
break;
|
|
case 'ADJUSTMENT':
|
|
// For adjustment, quantity is the new absolute value
|
|
$newQty = $quantity;
|
|
break;
|
|
default:
|
|
$newQty = $currentQty;
|
|
}
|
|
|
|
// Update or create WarehouseItem
|
|
$warehouseItemId = null;
|
|
if ($warehouseItem) {
|
|
$db->query("UPDATE WarehouseItem SET quantity = {$newQty} WHERE id = {$warehouseItem->id}");
|
|
$warehouseItemId = $warehouseItem->id;
|
|
} else {
|
|
$db->query("INSERT INTO WarehouseItem (articleId, warehouseLocationId, quantity, createBy, `create`)
|
|
VALUES ({$articleId}, {$locationId}, {$newQty}, {$this->user->id}, " . time() . ")");
|
|
$warehouseItemId = $db->insert_id;
|
|
}
|
|
|
|
// Create the movement record
|
|
$noteEscaped = $note ? "'" . $db->escape($note) . "'" : "NULL";
|
|
$db->query("INSERT INTO WarehouseMovement
|
|
(movementType, articleId, warehouseLocationId, warehouseItemId, quantity, quantityBefore, quantityAfter, reasonCategory, note, userId, createBy, `create`)
|
|
VALUES ('{$movementType}', {$articleId}, {$locationId}, {$warehouseItemId}, {$quantity}, {$currentQty}, {$newQty}, '{$db->escape($reasonCategory)}', {$noteEscaped}, {$this->user->id}, {$this->user->id}, " . time() . ")");
|
|
|
|
$movementId = $db->insert_id;
|
|
|
|
// Generate movement number
|
|
$movementNumber = WarehouseMovementModel::generateMovementNumber();
|
|
$db->query("UPDATE WarehouseMovement SET movementNumber = '{$movementNumber}' WHERE id = {$movementId}");
|
|
|
|
// Get type label for message
|
|
$typeLabels = ['IN' => 'Einbuchung', 'OUT' => 'Ausbuchung', 'ADJUSTMENT' => 'Korrektur'];
|
|
$typeLabel = $typeLabels[$movementType] ?? $movementType;
|
|
|
|
self::returnJson([
|
|
'success' => true,
|
|
'message' => "{$typeLabel} erfolgreich: {$quantity} x {$article->title}",
|
|
'movement' => [
|
|
'id' => $movementId,
|
|
'movementNumber' => $movementNumber,
|
|
'movementType' => $movementType,
|
|
'articleId' => $articleId,
|
|
'articleTitle' => $article->title,
|
|
'quantity' => $quantity,
|
|
'quantityBefore' => $currentQty,
|
|
'quantityAfter' => $newQty,
|
|
]
|
|
]);
|
|
}
|
|
|
|
public function getMyMovementsAction() {
|
|
$locationId = intval($this->request->locationId ?? 0);
|
|
$limit = intval($this->request->limit ?? 20);
|
|
|
|
$db = $this->db();
|
|
|
|
$whereClause = "m.userId = {$this->user->id}";
|
|
if ($locationId > 0) {
|
|
$whereClause .= " AND m.warehouseLocationId = {$locationId}";
|
|
}
|
|
|
|
$result = $db->query("SELECT m.*, wa.articleNumber, wa.title as articleTitle, wa.unit, wl.title as locationTitle
|
|
FROM WarehouseMovement m
|
|
LEFT JOIN WarehouseArticle wa ON wa.id = m.articleId
|
|
LEFT JOIN WarehouseLocation wl ON wl.id = m.warehouseLocationId
|
|
WHERE {$whereClause}
|
|
ORDER BY m.`create` DESC
|
|
LIMIT {$limit}");
|
|
|
|
$movements = [];
|
|
while ($row = $result->fetch_assoc()) {
|
|
$movements[] = [
|
|
'id' => intval($row['id']),
|
|
'movementNumber' => $row['movementNumber'],
|
|
'movementType' => $row['movementType'],
|
|
'articleId' => intval($row['articleId']),
|
|
'articleNumber' => $row['articleNumber'],
|
|
'articleTitle' => $row['articleTitle'],
|
|
'unit' => $row['unit'] ?? 'Stk.',
|
|
'locationTitle' => $row['locationTitle'],
|
|
'quantity' => floatval($row['quantity']),
|
|
'quantityBefore' => floatval($row['quantityBefore']),
|
|
'quantityAfter' => floatval($row['quantityAfter']),
|
|
'reasonCategory' => $row['reasonCategory'],
|
|
'note' => $row['note'],
|
|
'create' => date('d.m.Y H:i', $row['create']),
|
|
];
|
|
}
|
|
|
|
self::returnJson(['success' => true, 'movements' => $movements]);
|
|
}
|
|
|
|
public function getMovementTypesAction() {
|
|
$types = [
|
|
['value' => 'IN', 'text' => 'Einbuchung', 'icon' => 'plus-circle', 'color' => 'green'],
|
|
['value' => 'OUT', 'text' => 'Ausbuchung', 'icon' => 'minus-circle', 'color' => 'red'],
|
|
['value' => 'ADJUSTMENT', 'text' => 'Korrektur', 'icon' => 'edit', 'color' => 'yellow'],
|
|
];
|
|
|
|
self::returnJson(['success' => true, 'types' => $types]);
|
|
}
|
|
|
|
public function getPendingOrdersAction() {
|
|
$db = $this->db();
|
|
|
|
$result = $db->query("SELECT wo.*, wd.name as distributorName
|
|
FROM WarehouseOrder wo
|
|
LEFT JOIN WarehouseDistributor wd ON wd.id = wo.distributorId
|
|
WHERE wo.status IN ('sent', 'partiallyDelivered')
|
|
ORDER BY wo.`create` DESC");
|
|
|
|
$orders = [];
|
|
while ($row = $result->fetch_assoc()) {
|
|
$positions = json_decode($row['positions'], true) ?: [];
|
|
$totalItems = array_sum(array_column($positions, 'amount'));
|
|
|
|
// Calculate days since sent
|
|
$daysSinceSent = 0;
|
|
if (!empty($row['create'])) {
|
|
$daysSinceSent = floor((time() - intval($row['create'])) / 86400);
|
|
}
|
|
|
|
$orders[] = [
|
|
'id' => intval($row['id']),
|
|
'orderNumber' => $row['orderNumber'],
|
|
'distributorName' => $row['distributorName'] ?? 'Unbekannt',
|
|
'status' => $row['status'],
|
|
'statusLabel' => $row['status'] === 'sent' ? 'Versendet' : 'Teilweise geliefert',
|
|
'totalItems' => $totalItems,
|
|
'positionCount' => count($positions),
|
|
'daysSinceSent' => $daysSinceSent,
|
|
'create' => date('d.m.Y', $row['create']),
|
|
];
|
|
}
|
|
|
|
self::returnJson(['success' => true, 'orders' => $orders]);
|
|
}
|
|
|
|
public function getOrderForReceivingAction() {
|
|
$orderId = intval($this->request->orderId ?? 0);
|
|
|
|
if ($orderId <= 0) {
|
|
self::returnJson(['success' => false, 'message' => 'Keine Bestellung angegeben']);
|
|
return;
|
|
}
|
|
|
|
$order = WarehouseOrderModel::get($orderId);
|
|
if (!$order) {
|
|
self::returnJson(['success' => false, 'message' => 'Bestellung nicht gefunden']);
|
|
return;
|
|
}
|
|
|
|
if (!in_array($order->status, ['sent', 'partiallyDelivered'])) {
|
|
self::returnJson(['success' => false, 'message' => 'Bestellung ist nicht im Status Versendet oder Teilweise geliefert']);
|
|
return;
|
|
}
|
|
|
|
$distributor = WarehouseDistributorModel::get($order->distributorId);
|
|
$positions = json_decode($order->positions, true) ?: [];
|
|
|
|
// Get already delivered quantities from linked movements
|
|
$linkedMovementIds = $order->linkedMovementIds ? json_decode($order->linkedMovementIds, true) : [];
|
|
$deliveredByArticle = [];
|
|
|
|
foreach ($linkedMovementIds as $movementId) {
|
|
$movement = WarehouseMovementModel::get($movementId);
|
|
if ($movement && $movement->movementType === 'IN') {
|
|
if (!isset($deliveredByArticle[$movement->articleId])) {
|
|
$deliveredByArticle[$movement->articleId] = 0;
|
|
}
|
|
$deliveredByArticle[$movement->articleId] += $movement->quantity;
|
|
}
|
|
}
|
|
|
|
// Enrich positions with article details and delivered quantities
|
|
$enrichedPositions = [];
|
|
foreach ($positions as $index => $pos) {
|
|
$articleId = intval($pos['article']);
|
|
$article = WarehouseArticleModel::get($articleId);
|
|
|
|
$orderedQty = floatval($pos['amount']);
|
|
$deliveredQty = $deliveredByArticle[$articleId] ?? 0;
|
|
$remainingQty = max(0, $orderedQty - $deliveredQty);
|
|
|
|
$enrichedPositions[] = [
|
|
'index' => $index,
|
|
'articleId' => $articleId,
|
|
'articleNumber' => $article ? $article->articleNumber : '',
|
|
'articleTitle' => $article ? $article->title : ($pos['article_text'] ?? 'Unbekannt'),
|
|
'unit' => $article ? ($article->unit ?? 'Stk.') : 'Stk.',
|
|
'orderedQty' => $orderedQty,
|
|
'deliveredQty' => $deliveredQty,
|
|
'remainingQty' => $remainingQty,
|
|
'receivingQty' => $remainingQty, // Default to remaining
|
|
];
|
|
}
|
|
|
|
self::returnJson([
|
|
'success' => true,
|
|
'order' => [
|
|
'id' => $order->id,
|
|
'orderNumber' => $order->orderNumber,
|
|
'distributorName' => $distributor ? $distributor->name : 'Unbekannt',
|
|
'status' => $order->status,
|
|
'note' => $order->note,
|
|
'create' => date('d.m.Y H:i', $order->create),
|
|
],
|
|
'positions' => $enrichedPositions
|
|
]);
|
|
}
|
|
|
|
public function submitOrderReceivingAction() {
|
|
$postData = $this->getPostData();
|
|
|
|
$orderId = intval($postData['orderId'] ?? 0);
|
|
$locationId = intval($postData['locationId'] ?? 0);
|
|
$positions = $postData['positions'] ?? [];
|
|
$deliveryNoteFileId = $postData['deliveryNoteFileId'] ?? null;
|
|
$note = $postData['note'] ?? null;
|
|
|
|
// Validation
|
|
if ($orderId <= 0) {
|
|
self::returnJson(['success' => false, 'message' => 'Keine Bestellung angegeben']);
|
|
return;
|
|
}
|
|
|
|
if ($locationId <= 0) {
|
|
self::returnJson(['success' => false, 'message' => 'Kein Lagerort ausgewählt']);
|
|
return;
|
|
}
|
|
|
|
if (empty($positions)) {
|
|
self::returnJson(['success' => false, 'message' => 'Keine Positionen angegeben']);
|
|
return;
|
|
}
|
|
|
|
$order = WarehouseOrderModel::get($orderId);
|
|
if (!$order) {
|
|
self::returnJson(['success' => false, 'message' => 'Bestellung nicht gefunden']);
|
|
return;
|
|
}
|
|
|
|
if (!in_array($order->status, ['sent', 'partiallyDelivered'])) {
|
|
self::returnJson(['success' => false, 'message' => 'Bestellung ist nicht im Status Versendet oder Teilweise geliefert']);
|
|
return;
|
|
}
|
|
|
|
$db = $this->db();
|
|
$createdMovementIds = [];
|
|
$totalReceived = 0;
|
|
|
|
// Create movements for each position with quantity > 0
|
|
foreach ($positions as $pos) {
|
|
$articleId = intval($pos['articleId'] ?? 0);
|
|
$quantity = floatval($pos['quantity'] ?? 0);
|
|
|
|
if ($articleId <= 0 || $quantity <= 0) {
|
|
continue;
|
|
}
|
|
|
|
// Find or create WarehouseItem
|
|
$existingItems = WarehouseItemModel::getAll([
|
|
'articleId' => $articleId,
|
|
'warehouseLocationId' => $locationId
|
|
]);
|
|
|
|
$warehouseItem = count($existingItems) > 0 ? $existingItems[0] : null;
|
|
$currentQty = $warehouseItem ? floatval($warehouseItem->quantity) : 0;
|
|
$newQty = $currentQty + $quantity;
|
|
|
|
// Update or create WarehouseItem
|
|
if ($warehouseItem) {
|
|
$db->query("UPDATE WarehouseItem SET quantity = {$newQty} WHERE id = {$warehouseItem->id}");
|
|
$warehouseItemId = $warehouseItem->id;
|
|
} else {
|
|
$db->query("INSERT INTO WarehouseItem (articleId, warehouseLocationId, quantity, createBy, `create`)
|
|
VALUES ({$articleId}, {$locationId}, {$newQty}, {$this->user->id}, " . time() . ")");
|
|
$warehouseItemId = $db->insert_id;
|
|
}
|
|
|
|
// Create movement record
|
|
$movementNote = "Lagereingang aus Bestellung {$order->orderNumber}";
|
|
if ($note) {
|
|
$movementNote .= " - " . $note;
|
|
}
|
|
$noteEscaped = "'" . $db->escape($movementNote) . "'";
|
|
|
|
$db->query("INSERT INTO WarehouseMovement
|
|
(movementType, articleId, warehouseLocationId, warehouseItemId, quantity, quantityBefore, quantityAfter, reasonCategory, linkedOrderId, note, userId, createBy, `create`)
|
|
VALUES ('IN', {$articleId}, {$locationId}, {$warehouseItemId}, {$quantity}, {$currentQty}, {$newQty}, 'Warenlieferung', {$orderId}, {$noteEscaped}, {$this->user->id}, {$this->user->id}, " . time() . ")");
|
|
|
|
$movementId = $db->insert_id;
|
|
|
|
// Generate movement number
|
|
$movementNumber = WarehouseMovementModel::generateMovementNumber();
|
|
$db->query("UPDATE WarehouseMovement SET movementNumber = '{$movementNumber}' WHERE id = {$movementId}");
|
|
|
|
$createdMovementIds[] = $movementId;
|
|
$totalReceived += $quantity;
|
|
}
|
|
|
|
if (empty($createdMovementIds)) {
|
|
self::returnJson(['success' => false, 'message' => 'Keine Mengen eingegeben']);
|
|
return;
|
|
}
|
|
|
|
// Update order with linked movement IDs
|
|
$existingMovementIds = $order->linkedMovementIds ? json_decode($order->linkedMovementIds, true) : [];
|
|
$allMovementIds = array_merge($existingMovementIds, $createdMovementIds);
|
|
|
|
// Update delivery note file IDs if provided
|
|
$existingFileIds = $order->deliveryNoteFileIds ? json_decode($order->deliveryNoteFileIds, true) : [];
|
|
if ($deliveryNoteFileId) {
|
|
$existingFileIds[] = $deliveryNoteFileId;
|
|
}
|
|
|
|
// Determine new status - check if all items are now fully delivered
|
|
$orderPositions = json_decode($order->positions, true) ?: [];
|
|
$allFullyDelivered = true;
|
|
|
|
// Get all delivered quantities including new ones
|
|
$deliveredByArticle = [];
|
|
foreach ($allMovementIds as $movementId) {
|
|
$movement = WarehouseMovementModel::get($movementId);
|
|
if ($movement && $movement->movementType === 'IN') {
|
|
if (!isset($deliveredByArticle[$movement->articleId])) {
|
|
$deliveredByArticle[$movement->articleId] = 0;
|
|
}
|
|
$deliveredByArticle[$movement->articleId] += $movement->quantity;
|
|
}
|
|
}
|
|
|
|
foreach ($orderPositions as $pos) {
|
|
$articleId = intval($pos['article']);
|
|
$orderedQty = floatval($pos['amount']);
|
|
$deliveredQty = $deliveredByArticle[$articleId] ?? 0;
|
|
|
|
if ($deliveredQty < $orderedQty) {
|
|
$allFullyDelivered = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
$newStatus = $allFullyDelivered ? 'fullyDelivered' : 'partiallyDelivered';
|
|
|
|
// Update order
|
|
$orderAsArray = (array)$order;
|
|
$orderAsArray['linkedMovementIds'] = json_encode($allMovementIds);
|
|
$orderAsArray['deliveryNoteFileIds'] = json_encode($existingFileIds);
|
|
$orderAsArray['status'] = $newStatus;
|
|
WarehouseOrderModel::update($orderAsArray);
|
|
|
|
// Create log entry
|
|
$logMessage = count($createdMovementIds) . " Lagerbewegung(en) erstellt via Mobile App.";
|
|
if ($note) {
|
|
$logMessage .= "\n" . $note;
|
|
}
|
|
|
|
WarehouseLogModel::create([
|
|
'table' => 'WarehouseOrder',
|
|
'rowId' => $orderId,
|
|
'type' => 'statusChange',
|
|
'message' => "Status geändert auf " . ($newStatus === 'fullyDelivered' ? 'Geliefert' : 'Teilweise geliefert') . ".\n" . $logMessage,
|
|
'createBy' => $this->user->id,
|
|
'create' => time()
|
|
]);
|
|
|
|
self::returnJson([
|
|
'success' => true,
|
|
'message' => "{$totalReceived} Artikel empfangen. " . count($createdMovementIds) . " Lagerbewegung(en) erstellt.",
|
|
'newStatus' => $newStatus,
|
|
'createdMovementIds' => $createdMovementIds
|
|
]);
|
|
}
|
|
}
|