From bb2e39c2c3613cf08b8d4411da364a722e86b524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sedat=20=C3=96zt=C3=BCrk?= Date: Tue, 16 Sep 2025 00:11:40 +0300 Subject: [PATCH] Warehouse Management --- .../components/InventoryTracking.tsx | 937 +++++----- .../warehouse/components/LocationTracking.tsx | 346 ++-- ui/src/views/warehouse/components/LotForm.tsx | 185 +- .../components/MaterialMovements.tsx | 676 ++++--- .../warehouse/components/PutawayRules.tsx | 991 +++++----- .../views/warehouse/components/SerialForm.tsx | 160 +- .../components/StockLevelsInventory.tsx | 1296 ++++++------- .../components/WarehouseDefinitions.tsx | 572 +++--- .../warehouse/components/WarehouseForm.tsx | 903 +++++----- .../warehouse/components/WarehouseIssue.tsx | 1601 ++++++++--------- .../warehouse/components/WarehouseList.tsx | 742 ++++---- .../warehouse/components/WarehouseReceipt.tsx | 1059 +++++------ .../components/WarehouseTransfer.tsx | 1265 ++++++------- 13 files changed, 4751 insertions(+), 5982 deletions(-) diff --git a/ui/src/views/warehouse/components/InventoryTracking.tsx b/ui/src/views/warehouse/components/InventoryTracking.tsx index 9cb0082c..44392fa5 100644 --- a/ui/src/views/warehouse/components/InventoryTracking.tsx +++ b/ui/src/views/warehouse/components/InventoryTracking.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState } from 'react' import { FaBox, FaMapMarkerAlt, @@ -8,521 +8,474 @@ import { FaEye, FaEdit, FaPlus, -} from "react-icons/fa"; +} from 'react-icons/fa' // using modals for create actions; no navigation needed here -import { - MmLotNumber, - MmSerialNumber, - QualityStatusEnum, - SerialStatusEnum, -} from "../../../types/mm"; -import LotForm from "./LotForm"; -import SerialForm from "./SerialForm"; -import { mockLotNumbers } from "../../../mocks/mockLotNumbers"; -import { mockSerialNumbers } from "../../../mocks/mockSerialNumbers"; -import { mockMaterials } from "../../../mocks/mockMaterials"; -import { mockUnits } from "../../../mocks/mockUnits"; -import { mockBusinessParties } from "../../../mocks/mockBusinessParties"; -import Widget from "../../../components/common/Widget"; -import { PartyType } from "../../../types/common"; -import { getQualityStatusInfo, getSerialStatusInfo } from "../../../utils/erp"; +import { MmLotNumber, MmSerialNumber, QualityStatusEnum, SerialStatusEnum } from '../../../types/mm' +import LotForm from './LotForm' +import SerialForm from './SerialForm' +import { mockLotNumbers } from '../../../mocks/mockLotNumbers' +import { mockSerialNumbers } from '../../../mocks/mockSerialNumbers' +import { mockMaterials } from '../../../mocks/mockMaterials' +import { mockUnits } from '../../../mocks/mockUnits' +import { mockBusinessParties } from '../../../mocks/mockBusinessParties' +import Widget from '../../../components/common/Widget' +import { PartyType } from '../../../types/common' +import { getQualityStatusInfo, getSerialStatusInfo } from '../../../utils/erp' +import { Container } from '@/components/shared' const InventoryTracking: React.FC = () => { - const [activeTab, setActiveTab] = useState<"lots" | "serials">("lots"); - const [searchTerm, setSearchTerm] = useState(""); - const [showCreateMenu, setShowCreateMenu] = useState(false); + const [activeTab, setActiveTab] = useState<'lots' | 'serials'>('lots') + const [searchTerm, setSearchTerm] = useState('') + const [showCreateMenu, setShowCreateMenu] = useState(false) // Mock data için - const [lotNumbers, setLotNumbers] = useState(mockLotNumbers); + const [lotNumbers, setLotNumbers] = useState(mockLotNumbers) - const [serialNumbers, setSerialNumbers] = - useState(mockSerialNumbers); + const [serialNumbers, setSerialNumbers] = useState(mockSerialNumbers) // Modal control state - const [openLotModal, setOpenLotModal] = useState(false); - const [openSerialModal, setOpenSerialModal] = useState(false); - const [currentLot, setCurrentLot] = useState(null); - const [currentSerial, setCurrentSerial] = useState( - null - ); + const [openLotModal, setOpenLotModal] = useState(false) + const [openSerialModal, setOpenSerialModal] = useState(false) + const [currentLot, setCurrentLot] = useState(null) + const [currentSerial, setCurrentSerial] = useState(null) const filteredLots = lotNumbers.filter( (lot) => lot.lotNumber.toLowerCase().includes(searchTerm.toLowerCase()) || - lot.material?.name.toLowerCase().includes(searchTerm.toLowerCase()) - ); + lot.material?.name.toLowerCase().includes(searchTerm.toLowerCase()), + ) const filteredSerials = serialNumbers.filter( (serial) => serial.serialNumber.toLowerCase().includes(searchTerm.toLowerCase()) || - serial.material?.name.toLowerCase().includes(searchTerm.toLowerCase()) - ); + serial.material?.name.toLowerCase().includes(searchTerm.toLowerCase()), + ) return ( -
- {/* Header */} -
-
-

Envanter Takibi

-

- Lot ve seri numarası takiplerini yönetin -

-
-
- + +
+ {/* Header */} +
+
+

Envanter Takibi

+

Lot ve seri numarası takiplerini yönetin

+
+
+ - {/* only modal-based create menu (route-based entries removed) */} - {showCreateMenu && ( -
- - -
- )} -
-
- - {/* Summary Statistics */} -
- - - lot.qualityStatus === QualityStatusEnum.Approved - ).length - } - color="green" - icon="FaBox" - /> - - - - serial.status === SerialStatusEnum.Available - ).length - } - color="orange" - icon="FaHashtag" - /> -
- - {/* Search */} -
- - setSearchTerm(e.target.value)} - className="w-full pl-8 pr-3 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent" - /> -
- - {/* Tabs */} -
- -
- - {/* Lot Numbers Tab */} - {activeTab === "lots" && ( -
- - - - - - - - - - - - - - - {filteredLots.map((lot) => { - const qualityInfo = getQualityStatusInfo(lot.qualityStatus); - const isExpiringSoon = - lot.expiryDate && - new Date(lot.expiryDate).getTime() - new Date().getTime() < - 30 * 24 * 60 * 60 * 1000; - - return ( - - - - - - - - - - - ); - })} - -
- Lot Numarası - - Malzeme - - Miktar - - Üretim Tarihi - - Son Kullanma - - Tedarikçi - - Kalite Durumu - - İşlemler -
-
- -
- {lot.lotNumber} -
-
-
-
- {lot.material?.name} -
-
-
- {lot.quantity} {lot.unitId} -
-
-
- -
- {new Date(lot.productionDate).toLocaleDateString( - "tr-TR" - )} -
-
-
- {lot.expiryDate ? ( -
- {new Date(lot.expiryDate).toLocaleDateString("tr-TR")} - {isExpiringSoon && ( -
- Yakında Dolacak -
- )} -
- ) : ( - - - )} -
-
- {lot.supplierId || "-"} -
-
- - {qualityInfo.label} - - -
- - -
-
- { - setOpenLotModal(false); - setCurrentLot(null); - }} - onSave={(lot) => setLotNumbers((prev) => [lot, ...prev])} - onUpdate={(updated) => - setLotNumbers((prev) => - prev.map((l) => (l.id === updated.id ? updated : l)) - ) - } - materials={mockMaterials} - units={mockUnits} - suppliers={mockBusinessParties.filter( - (bp) => bp.partyType === PartyType.Supplier + {/* only modal-based create menu (route-based entries removed) */} + {showCreateMenu && ( +
+ + +
)} - initial={currentLot} - mode={currentLot ? "edit" : "create"} - /> - - {filteredLots.length === 0 && ( -
- -

- Lot bulunamadı -

-

- Arama kriterlerinize uygun lot kaydı bulunmuyor. -

-
- )} +
- )} - {/* Serial Numbers Tab */} - {activeTab === "serials" && ( -
- - - - - - - - - - - - - - - {filteredSerials.map((serial) => { - const statusInfo = getSerialStatusInfo(serial.status); - const isWarrantyExpiring = - serial.warrantyExpiryDate && - new Date(serial.warrantyExpiryDate).getTime() - - new Date().getTime() < - 30 * 24 * 60 * 60 * 1000; + {/* Summary Statistics */} +
+ - return ( -
- - - - - - - - - - ); - })} - -
- Seri Numarası - - Malzeme - - Lot - - Üretim Tarihi - - Garanti Bitiş - - Mevcut Lokasyon - - Durum - - İşlemler -
-
- -
- {serial.serialNumber} -
-
-
-
- {serial.material?.name} -
-
-
- {serial.lotId ? ( - - LOT-{serial.lotId} - - ) : ( - "-" - )} -
-
-
- -
- {new Date(serial.productionDate).toLocaleDateString( - "tr-TR" - )} -
-
-
- {serial.warrantyExpiryDate ? ( -
- {new Date( - serial.warrantyExpiryDate - ).toLocaleDateString("tr-TR")} - {isWarrantyExpiring && ( -
- Yakında Dolacak -
- )} -
- ) : ( - - - )} -
-
- -
- {serial.currentLocationId || "-"} -
-
-
- - {statusInfo.label} - - -
- - -
-
- - { - setOpenSerialModal(false); - setCurrentSerial(null); - }} - onSave={(serial) => setSerialNumbers((prev) => [serial, ...prev])} - onUpdate={(updated) => - setSerialNumbers((prev) => - prev.map((s) => (s.id === updated.id ? updated : s)) - ) + lot.qualityStatus === QualityStatusEnum.Approved).length } - materials={mockMaterials} - lots={lotNumbers.map((l) => ({ id: l.id, label: l.lotNumber }))} - initial={currentSerial} - mode={currentSerial ? "edit" : "create"} + color="green" + icon="FaBox" /> - {filteredSerials.length === 0 && ( -
- -

- Seri numarası bulunamadı -

-

- Arama kriterlerinize uygun seri numarası bulunmuyor. -

-
- )} -
- )} -
- ); -}; + -export default InventoryTracking; + serial.status === SerialStatusEnum.Available).length + } + color="orange" + icon="FaHashtag" + /> +
+ + {/* Search */} +
+ + setSearchTerm(e.target.value)} + className="w-full pl-8 pr-3 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent" + /> +
+ + {/* Tabs */} +
+ +
+ + {/* Lot Numbers Tab */} + {activeTab === 'lots' && ( +
+ + + + + + + + + + + + + + + {filteredLots.map((lot) => { + const qualityInfo = getQualityStatusInfo(lot.qualityStatus) + const isExpiringSoon = + lot.expiryDate && + new Date(lot.expiryDate).getTime() - new Date().getTime() < + 30 * 24 * 60 * 60 * 1000 + + return ( + + + + + + + + + + + ) + })} + +
+ Lot Numarası + + Malzeme + + Miktar + + Üretim Tarihi + + Son Kullanma + + Tedarikçi + + Kalite Durumu + + İşlemler +
+
+ +
{lot.lotNumber}
+
+
+
+ {lot.material?.name} +
+
+
+ {lot.quantity} {lot.unitId} +
+
+
+ +
+ {new Date(lot.productionDate).toLocaleDateString('tr-TR')} +
+
+
+ {lot.expiryDate ? ( +
+ {new Date(lot.expiryDate).toLocaleDateString('tr-TR')} + {isExpiringSoon && ( +
Yakında Dolacak
+ )} +
+ ) : ( + - + )} +
+
{lot.supplierId || '-'}
+
+ + {qualityInfo.label} + + +
+ + +
+
+ { + setOpenLotModal(false) + setCurrentLot(null) + }} + onSave={(lot) => setLotNumbers((prev) => [lot, ...prev])} + onUpdate={(updated) => + setLotNumbers((prev) => prev.map((l) => (l.id === updated.id ? updated : l))) + } + materials={mockMaterials} + units={mockUnits} + suppliers={mockBusinessParties.filter((bp) => bp.partyType === PartyType.Supplier)} + initial={currentLot} + mode={currentLot ? 'edit' : 'create'} + /> + + {filteredLots.length === 0 && ( +
+ +

Lot bulunamadı

+

+ Arama kriterlerinize uygun lot kaydı bulunmuyor. +

+
+ )} +
+ )} + + {/* Serial Numbers Tab */} + {activeTab === 'serials' && ( +
+ + + + + + + + + + + + + + + {filteredSerials.map((serial) => { + const statusInfo = getSerialStatusInfo(serial.status) + const isWarrantyExpiring = + serial.warrantyExpiryDate && + new Date(serial.warrantyExpiryDate).getTime() - new Date().getTime() < + 30 * 24 * 60 * 60 * 1000 + + return ( + + + + + + + + + + + ) + })} + +
+ Seri Numarası + + Malzeme + + Lot + + Üretim Tarihi + + Garanti Bitiş + + Mevcut Lokasyon + + Durum + + İşlemler +
+
+ +
+ {serial.serialNumber} +
+
+
+
+ {serial.material?.name} +
+
+
+ {serial.lotId ? ( + + LOT-{serial.lotId} + + ) : ( + '-' + )} +
+
+
+ +
+ {new Date(serial.productionDate).toLocaleDateString('tr-TR')} +
+
+
+ {serial.warrantyExpiryDate ? ( +
+ {new Date(serial.warrantyExpiryDate).toLocaleDateString('tr-TR')} + {isWarrantyExpiring && ( +
Yakında Dolacak
+ )} +
+ ) : ( + - + )} +
+
+ +
+ {serial.currentLocationId || '-'} +
+
+
+ + {statusInfo.label} + + +
+ + +
+
+ + { + setOpenSerialModal(false) + setCurrentSerial(null) + }} + onSave={(serial) => setSerialNumbers((prev) => [serial, ...prev])} + onUpdate={(updated) => + setSerialNumbers((prev) => prev.map((s) => (s.id === updated.id ? updated : s))) + } + materials={mockMaterials} + lots={lotNumbers.map((l) => ({ id: l.id, label: l.lotNumber }))} + initial={currentSerial} + mode={currentSerial ? 'edit' : 'create'} + /> + + {filteredSerials.length === 0 && ( +
+ +

Seri numarası bulunamadı

+

+ Arama kriterlerinize uygun seri numarası bulunmuyor. +

+
+ )} +
+ )} +
+ + ) +} + +export default InventoryTracking diff --git a/ui/src/views/warehouse/components/LocationTracking.tsx b/ui/src/views/warehouse/components/LocationTracking.tsx index 8578df84..4510d85c 100644 --- a/ui/src/views/warehouse/components/LocationTracking.tsx +++ b/ui/src/views/warehouse/components/LocationTracking.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState } from 'react' import { FaSearch, FaMapMarkerAlt, @@ -7,45 +7,43 @@ import { FaEye, FaTh, FaList, -} from "react-icons/fa"; -import { mockWarehouses } from "../../../mocks/mockWarehouses"; -import { mockLocations } from "../../../mocks/mockLocations"; -import { mockStockItems } from "../../../mocks/mockStockItems"; -import { getStockStatusColor, getStockStatusText } from "../../../utils/erp"; +} from 'react-icons/fa' +import { mockWarehouses } from '../../../mocks/mockWarehouses' +import { mockLocations } from '../../../mocks/mockLocations' +import { mockStockItems } from '../../../mocks/mockStockItems' +import { getStockStatusColor, getStockStatusText } from '../../../utils/erp' +import { Container } from '@/components/shared' const LocationTracking: React.FC = () => { - const [searchTerm, setSearchTerm] = useState(""); - const [selectedWarehouse, setSelectedWarehouse] = useState(""); - const [selectedLocation, setSelectedLocation] = useState(""); - const [viewMode, setViewMode] = useState<"grid" | "list">("grid"); + const [searchTerm, setSearchTerm] = useState('') + const [selectedWarehouse, setSelectedWarehouse] = useState('') + const [selectedLocation, setSelectedLocation] = useState('') + const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid') const getLocationUtilization = (locationId: string) => { - const location = mockLocations.find((l) => l.id === locationId); - if (!location) return 0; - return (location.currentStock / location.capacity) * 100; - }; + const location = mockLocations.find((l) => l.id === locationId) + if (!location) return 0 + return (location.currentStock / location.capacity) * 100 + } const getLocationStockItems = (locationId: string) => { - return mockStockItems.filter((item) => item.locationId === locationId); - }; + return mockStockItems.filter((item) => item.locationId === locationId) + } const filteredLocations = mockLocations.filter((location) => { const matchesSearch = location.name.toLowerCase().includes(searchTerm.toLowerCase()) || - location.locationCode.toLowerCase().includes(searchTerm.toLowerCase()); - const matchesWarehouse = - selectedWarehouse === "" || location.warehouseId === selectedWarehouse; - return matchesSearch && matchesWarehouse; - }); + location.locationCode.toLowerCase().includes(searchTerm.toLowerCase()) + const matchesWarehouse = selectedWarehouse === '' || location.warehouseId === selectedWarehouse + return matchesSearch && matchesWarehouse + }) const GridView = () => (
{filteredLocations.map((location) => { - const warehouse = mockWarehouses.find( - (w) => w.id === location.warehouseId - ); - const locationStockItems = getLocationStockItems(location.id); - const utilization = getLocationUtilization(location.id); + const warehouse = mockWarehouses.find((w) => w.id === location.warehouseId) + const locationStockItems = getLocationStockItems(location.id) + const utilization = getLocationUtilization(location.id) return (
{

{location.name}

-

- {location.locationCode} -

+

{location.locationCode}

- ); + ) })} - ); + ) const LocationDetailModal = () => { - const location = mockLocations.find((l) => l.id === selectedLocation); - const locationStockItems = getLocationStockItems(selectedLocation); + const location = mockLocations.find((l) => l.id === selectedLocation) + const locationStockItems = getLocationStockItems(selectedLocation) - if (!selectedLocation || !location) return null; + if (!selectedLocation || !location) return null return (
setSelectedLocation("")} + onClick={() => setSelectedLocation('')} />
-

- {location.name} - Detaylar -

+

{location.name} - Detaylar

+ +
-
-
- {/* Filters */} -
-
- - setSearchTerm(e.target.value)} - className="pl-10 pr-4 py-1.5 text-sm w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" - /> -
- + {/* Content */} + {viewMode === 'grid' ? : }
- {/* Content */} - {viewMode === "grid" ? : } - {/* Location Detail Modal */} -
- ); -}; + + ) +} -export default LocationTracking; +export default LocationTracking diff --git a/ui/src/views/warehouse/components/LotForm.tsx b/ui/src/views/warehouse/components/LotForm.tsx index 35a23ef3..01de046a 100644 --- a/ui/src/views/warehouse/components/LotForm.tsx +++ b/ui/src/views/warehouse/components/LotForm.tsx @@ -1,31 +1,26 @@ -import React from "react"; -import { useFormik } from "formik"; -import * as Yup from "yup"; -import { FaSave, FaTimes } from "react-icons/fa"; -import { - QualityStatusEnum, - MmLotNumber, - MmUnit, - MmMaterial, -} from "../../../types/mm"; -import { BusinessParty } from "../../../types/common"; +import React from 'react' +import { useFormik } from 'formik' +import * as Yup from 'yup' +import { FaSave, FaTimes } from 'react-icons/fa' +import { QualityStatusEnum, MmLotNumber, MmUnit, MmMaterial } from '../../../types/mm' +import { BusinessParty } from '../../../types/common' const validationSchema = Yup.object({ - materialId: Yup.string().required("Malzeme seçimi zorunlu"), - lotNumber: Yup.string().required("Lot numarası zorunlu"), + materialId: Yup.string().required('Malzeme seçimi zorunlu'), + lotNumber: Yup.string().required('Lot numarası zorunlu'), quantity: Yup.number().min(0, "Miktar 0'dan büyük olmalıdır"), -}); +}) interface LotFormProps { - isOpen: boolean; - onClose: () => void; - onSave: (lot: MmLotNumber) => void; - onUpdate?: (lot: MmLotNumber) => void; - materials: MmMaterial[]; - suppliers?: BusinessParty[]; - units?: MmUnit[]; - initial?: MmLotNumber | null; - mode?: "create" | "edit" | "view"; + isOpen: boolean + onClose: () => void + onSave: (lot: MmLotNumber) => void + onUpdate?: (lot: MmLotNumber) => void + materials: MmMaterial[] + suppliers?: BusinessParty[] + units?: MmUnit[] + initial?: MmLotNumber | null + mode?: 'create' | 'edit' | 'view' } const LotForm: React.FC = ({ @@ -37,17 +32,17 @@ const LotForm: React.FC = ({ suppliers = [], units = [], initial = null, - mode = "create", + mode = 'create', }) => { const formik = useFormik({ initialValues: { - materialId: "", - lotNumber: "", - productionDate: "", - expiryDate: "", + materialId: '', + lotNumber: '', + productionDate: '', + expiryDate: '', quantity: 0, - unitId: "KG", - supplierId: suppliers.length ? suppliers[0].id : "", + unitId: 'KG', + supplierId: suppliers.length ? suppliers[0].id : '', qualityStatus: QualityStatusEnum.Pending, isActive: true, }, @@ -57,52 +52,48 @@ const LotForm: React.FC = ({ id: (initial && initial.id) || Date.now().toString(), materialId: values.materialId, lotNumber: values.lotNumber, - productionDate: values.productionDate - ? new Date(values.productionDate) - : new Date(), + productionDate: values.productionDate ? new Date(values.productionDate) : new Date(), expiryDate: values.expiryDate ? new Date(values.expiryDate) : undefined, quantity: Number(values.quantity), unitId: values.unitId, supplierId: values.supplierId || undefined, qualityStatus: values.qualityStatus, isActive: !!values.isActive, - }; + } // simulate API - await new Promise((r) => setTimeout(r, 300)); - if (mode === "edit" && onUpdate) { - onUpdate(newLot); + await new Promise((r) => setTimeout(r, 300)) + if (mode === 'edit' && onUpdate) { + onUpdate(newLot) } else { - onSave(newLot); + onSave(newLot) } - onClose(); + onClose() }, - }); + }) // sync initial values when editing/viewing React.useEffect(() => { if (initial) { - const src = initial; + const src = initial formik.setValues({ - materialId: src.materialId || "", - lotNumber: src.lotNumber || "", + materialId: src.materialId || '', + lotNumber: src.lotNumber || '', productionDate: src.productionDate ? new Date(src.productionDate).toISOString().slice(0, 10) - : "", - expiryDate: src.expiryDate - ? new Date(src.expiryDate).toISOString().slice(0, 10) - : "", + : '', + expiryDate: src.expiryDate ? new Date(src.expiryDate).toISOString().slice(0, 10) : '', quantity: src.quantity || 0, - unitId: src.unitId || (units.length ? units[0].id : ""), - supplierId: src.supplierId || (suppliers.length ? suppliers[0].id : ""), + unitId: src.unitId || (units.length ? units[0].id : ''), + supplierId: src.supplierId || (suppliers.length ? suppliers[0].id : ''), qualityStatus: src.qualityStatus || QualityStatusEnum.Pending, isActive: !!src.isActive, - }); + }) } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [initial]); + }, [initial]) - if (!isOpen) return null; + if (!isOpen) return null return (
@@ -112,18 +103,18 @@ const LotForm: React.FC = ({

- {mode === "create" - ? "Yeni Lot Kaydı" - : mode === "edit" - ? "Lot Düzenle" - : "Lot Detayı"} + {mode === 'create' + ? 'Yeni Lot Kaydı' + : mode === 'edit' + ? 'Lot Düzenle' + : 'Lot Detayı'}

- {mode === "create" - ? "Lot bilgilerini girin" - : mode === "edit" - ? "Mevcut lot bilgilerini güncelleyin" - : "Lot bilgileri (sadece gösterim)"} + {mode === 'create' + ? 'Lot bilgilerini girin' + : mode === 'edit' + ? 'Mevcut lot bilgilerini güncelleyin' + : 'Lot bilgileri (sadece gösterim)'}

@@ -134,7 +125,7 @@ const LotForm: React.FC = ({ > Kapat - {mode !== "view" && ( + {mode !== 'view' && (
- - {/* Movements Table */} -
- {loading ? ( -
-
-

Hareketler yükleniyor...

-
- ) : ( -
- - - - - - {!materialId && ( - - )} - - - - - - - - - - - {movementsWithBalance.map((movement) => { - const typeInfo = getMovementTypeInfo(movement.movementType); - const Icon = typeInfo.icon; - - return ( - - - - {!materialId && ( - - )} - - - - - - - - ); - })} - -
- Tarih/Saat - - Hareket Tipi - - Malzeme - - Miktar - - Bakiye - - Depo/Lokasyon - - Lot/Seri - - Belge - - Açıklama - - Oluşturan -
-
- -
-
- {new Date( - movement.movementDate - ).toLocaleDateString("tr-TR")} -
-
- {new Date( - movement.movementDate - ).toLocaleTimeString("tr-TR")} -
-
-
-
-
- - {typeInfo.label} -
-
-
- {movement.material.code} - {movement.material.name} -
-
-
- 0 - ? "text-green-600" - : "text-red-600" - }`} - > - {movement.quantity > 0 ? "+" : ""} - {movement.quantity} - - - {movement.unit?.code} - -
-
-
- {movement.runningBalance} {movement.unit?.code} -
-
-
- {movement.toWarehouse?.code} - {movement.toZone?.zoneCode && ( -
- {movement.toZone?.zoneCode} -
- )} - {movement.toLocation?.locationCode && ( -
- {movement.toLocation?.locationCode} -
- )} -
-
-
- {movement.lotNumber && ( -
- {movement.lotNumber} -
- )} -
-
- {movement.referenceDocument && ( -
-
{movement.referenceDocument}
-
- {movement.referenceDocumentType} -
-
- )} -
-
- {movement.description} -
-
-
- )} - - {!loading && movements.length === 0 && ( -
- -

- Hareket bulunamadı -

-

- Seçilen kriterlere uygun hareket kaydı bulunmuyor. + +

+ {/* Header */} +
+
+

+ {materialId ? 'Stok Hareketleri' : 'Tüm Stok Hareketleri'} +

+

+ {materialId ? `${materialId} kodlu malzeme için` : 'Tüm malzemeler için'} stok + hareketlerini görüntüleyin

- )} -
-
- ); -}; -export default MaterialMovements; +
+ + +
+
+ + {/* Summary Cards */} +
+ {[ + { + title: 'Toplam Giriş', + value: '125 KG', + color: 'green', + icon: 'FaArrowDown', + }, + { + title: 'Toplam Çıkış', + value: '50 KG', + color: 'red', + icon: 'FaArrowUp', + }, + { + title: 'Net Bakiye', + value: '75 KG', + color: 'blue', + icon: 'FaBalanceScale', + }, + { + title: 'Hareket Sayısı', + value: movements.length, + color: 'gray', + icon: 'FaExchangeAlt', + }, + ].map((stat) => ( + + ))} +
+ + {/* Filters */} +
+
+
+ + setFilters({ ...filters, startDate: e.target.value })} + className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent" + /> +
+ +
+ + setFilters({ ...filters, endDate: e.target.value })} + className="w-full px-2 py-1 text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent" + /> +
+ +
+ + +
+ +
+ + +
+
+
+ + {/* Movements Table */} +
+ {loading ? ( +
+
+

Hareketler yükleniyor...

+
+ ) : ( +
+ + + + + + {!materialId && ( + + )} + + + + + + + + + + + {movementsWithBalance.map((movement) => { + const typeInfo = getMovementTypeInfo(movement.movementType) + const Icon = typeInfo.icon + + return ( + + + + {!materialId && ( + + )} + + + + + + + + ) + })} + +
+ Tarih/Saat + + Hareket Tipi + + Malzeme + + Miktar + + Bakiye + + Depo/Lokasyon + + Lot/Seri + + Belge + + Açıklama + + Oluşturan +
+
+ +
+
+ {new Date(movement.movementDate).toLocaleDateString('tr-TR')} +
+
+ {new Date(movement.movementDate).toLocaleTimeString('tr-TR')} +
+
+
+
+
+ + {typeInfo.label} +
+
+
+ {movement.material.code} - {movement.material.name} +
+
+
+ 0 ? 'text-green-600' : 'text-red-600' + }`} + > + {movement.quantity > 0 ? '+' : ''} + {movement.quantity} + + {movement.unit?.code} +
+
+
+ {movement.runningBalance} {movement.unit?.code} +
+
+
+ {movement.toWarehouse?.code} + {movement.toZone?.zoneCode && ( +
+ {movement.toZone?.zoneCode} +
+ )} + {movement.toLocation?.locationCode && ( +
+ {movement.toLocation?.locationCode} +
+ )} +
+
+
+ {movement.lotNumber && ( +
+ {movement.lotNumber} +
+ )} +
+
+ {movement.referenceDocument && ( +
+
{movement.referenceDocument}
+
+ {movement.referenceDocumentType} +
+
+ )} +
+
{movement.description}
+
+
+ )} + + {!loading && movements.length === 0 && ( +
+ +

Hareket bulunamadı

+

+ Seçilen kriterlere uygun hareket kaydı bulunmuyor. +

+
+ )} +
+
+ + ) +} + +export default MaterialMovements diff --git a/ui/src/views/warehouse/components/PutawayRules.tsx b/ui/src/views/warehouse/components/PutawayRules.tsx index 6a1491fd..fd59b27b 100644 --- a/ui/src/views/warehouse/components/PutawayRules.tsx +++ b/ui/src/views/warehouse/components/PutawayRules.tsx @@ -1,5 +1,5 @@ -import React, { useState } from "react"; -import { PutawayStrategyEnum } from "../../../types/wm"; +import React, { useState } from 'react' +import { PutawayStrategyEnum } from '../../../types/wm' import { FaSearch, FaPlus, @@ -13,174 +13,161 @@ import { FaArrowDown, FaTh, FaList, -} from "react-icons/fa"; -import { mockPutawayRules } from "../../../mocks/mockPutawayRules"; -import { mockWarehouses } from "../../../mocks/mockWarehouses"; -import { mockZones } from "../../../mocks/mockZones"; -import { mockLocations } from "../../../mocks/mockLocations"; -import { mockMaterialTypes } from "../../../mocks/mockMaterialTypes"; -import { mockMaterialGroups } from "../../../mocks/mockMaterialGroups"; +} from 'react-icons/fa' +import { mockPutawayRules } from '../../../mocks/mockPutawayRules' +import { mockWarehouses } from '../../../mocks/mockWarehouses' +import { mockZones } from '../../../mocks/mockZones' +import { mockLocations } from '../../../mocks/mockLocations' +import { mockMaterialTypes } from '../../../mocks/mockMaterialTypes' +import { mockMaterialGroups } from '../../../mocks/mockMaterialGroups' import { getPutawayStrategyColor, getPutawayStrategyText, getConditionTypeText, getConditionOperatorText, -} from "../../../utils/erp"; +} from '../../../utils/erp' +import { Container } from '@/components/shared' const PutawayRules: React.FC = () => { - const [searchTerm, setSearchTerm] = useState(""); - const [selectedStrategy, setSelectedStrategy] = useState< - PutawayStrategyEnum | "" - >(""); - const [viewMode, setViewMode] = useState<"grid" | "list">("grid"); - const [showRuleForm, setShowRuleForm] = useState(false); - const [editingRuleId, setEditingRuleId] = useState(null); - const [selectedRule, setSelectedRule] = useState(""); + const [searchTerm, setSearchTerm] = useState('') + const [selectedStrategy, setSelectedStrategy] = useState('') + const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid') + const [showRuleForm, setShowRuleForm] = useState(false) + const [editingRuleId, setEditingRuleId] = useState(null) + const [selectedRule, setSelectedRule] = useState('') // Form States const [formData, setFormData] = useState({ - name: "", - ruleCode: "", - description: "", - warehouseId: "", - materialTypeId: "", - materialGroupId: "", + name: '', + ruleCode: '', + description: '', + warehouseId: '', + materialTypeId: '', + materialGroupId: '', priority: 1, - targetZoneId: "", - targetLocationId: "", + targetZoneId: '', + targetLocationId: '', strategy: PutawayStrategyEnum.FIFO, isActive: true, - }); + }) // Helper functions for names const getWarehouseName = (warehouseId: string | undefined) => { - if (!warehouseId) return "Belirtilmemiş"; - const warehouse = mockWarehouses.find((w) => w.id === warehouseId); - return warehouse ? warehouse.name : `Depo ${warehouseId}`; - }; + if (!warehouseId) return 'Belirtilmemiş' + const warehouse = mockWarehouses.find((w) => w.id === warehouseId) + return warehouse ? warehouse.name : `Depo ${warehouseId}` + } const getZoneName = (zoneId: string | undefined) => { - if (!zoneId) return "Belirtilmemiş"; - const zone = mockZones.find((z) => z.id === zoneId); - return zone ? zone.name : `Bölge ${zoneId}`; - }; + if (!zoneId) return 'Belirtilmemiş' + const zone = mockZones.find((z) => z.id === zoneId) + return zone ? zone.name : `Bölge ${zoneId}` + } const getLocationName = (locationId: string | undefined) => { - if (!locationId) return "Belirtilmemiş"; - const location = mockLocations.find((l) => l.id === locationId); - return location ? location.name : `Lokasyon ${locationId}`; - }; + if (!locationId) return 'Belirtilmemiş' + const location = mockLocations.find((l) => l.id === locationId) + return location ? location.name : `Lokasyon ${locationId}` + } const getMaterialTypeName = (materialTypeId: string | undefined) => { - if (!materialTypeId) return "Belirtilmemiş"; - const materialType = mockMaterialTypes.find( - (mt) => mt.id === materialTypeId - ); - return materialType ? materialType.name : `Tip ${materialTypeId}`; - }; + if (!materialTypeId) return 'Belirtilmemiş' + const materialType = mockMaterialTypes.find((mt) => mt.id === materialTypeId) + return materialType ? materialType.name : `Tip ${materialTypeId}` + } const getMaterialGroupName = (materialGroupId: string | undefined) => { - if (!materialGroupId) return "Belirtilmemiş"; - const materialGroup = mockMaterialGroups.find( - (mg) => mg.id === materialGroupId - ); - return materialGroup ? materialGroup.name : `Grup ${materialGroupId}`; - }; + if (!materialGroupId) return 'Belirtilmemiş' + const materialGroup = mockMaterialGroups.find((mg) => mg.id === materialGroupId) + return materialGroup ? materialGroup.name : `Grup ${materialGroupId}` + } // Form Functions const resetForm = () => { setFormData({ - name: "", - ruleCode: "", - description: "", - warehouseId: "", - materialTypeId: "", - materialGroupId: "", + name: '', + ruleCode: '', + description: '', + warehouseId: '', + materialTypeId: '', + materialGroupId: '', priority: 1, - targetZoneId: "", - targetLocationId: "", + targetZoneId: '', + targetLocationId: '', strategy: PutawayStrategyEnum.FIFO, isActive: true, - }); - }; + }) + } const handleEditRule = (ruleId: string) => { - const rule = mockPutawayRules.find((r) => r.id === ruleId); + const rule = mockPutawayRules.find((r) => r.id === ruleId) if (rule) { setFormData({ name: rule.name, ruleCode: rule.code, - description: rule.description || "", + description: rule.description || '', warehouseId: rule.warehouseId, - materialTypeId: rule.materialTypeId || "", - materialGroupId: rule.materialGroupId || "", + materialTypeId: rule.materialTypeId || '', + materialGroupId: rule.materialGroupId || '', priority: rule.priority, - targetZoneId: rule.targetZoneId || "", - targetLocationId: rule.targetLocationId || "", + targetZoneId: rule.targetZoneId || '', + targetLocationId: rule.targetLocationId || '', strategy: rule.strategy, isActive: rule.isActive, - }); + }) } - setEditingRuleId(ruleId); - setShowRuleForm(true); - }; + setEditingRuleId(ruleId) + setShowRuleForm(true) + } const handleNewRule = () => { - resetForm(); - setEditingRuleId(null); - setShowRuleForm(true); - }; + resetForm() + setEditingRuleId(null) + setShowRuleForm(true) + } const handleCloseForm = () => { - setShowRuleForm(false); - setEditingRuleId(null); - resetForm(); - }; + setShowRuleForm(false) + setEditingRuleId(null) + resetForm() + } const handleSubmitForm = () => { // Form validation and submission logic here - console.log("Form Data:", formData); - handleCloseForm(); - }; + console.log('Form Data:', formData) + handleCloseForm() + } const filteredRules = mockPutawayRules.filter((rule) => { const matchesSearch = rule.name.toLowerCase().includes(searchTerm.toLowerCase()) || - rule.code.toLowerCase().includes(searchTerm.toLowerCase()); - const matchesStrategy = - selectedStrategy === "" || rule.strategy === selectedStrategy; - return matchesSearch && matchesStrategy; - }); + rule.code.toLowerCase().includes(searchTerm.toLowerCase()) + const matchesStrategy = selectedStrategy === '' || rule.strategy === selectedStrategy + return matchesSearch && matchesStrategy + }) const RuleDetailModal = () => { - const rule = mockPutawayRules.find((r) => r.id === selectedRule); + const rule = mockPutawayRules.find((r) => r.id === selectedRule) - if (!selectedRule || !rule) return null; + if (!selectedRule || !rule) return null return (
setSelectedRule("")} + onClick={() => setSelectedRule('')} />
-

- {rule.name} - Kural Detayları -

+

{rule.name} - Kural Detayları

+ +
-
-
-
- {/* Filters */} -
-
- - setSearchTerm(e.target.value)} - className="pl-10 pr-4 py-1.5 text-sm w-full border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent" - /> -
- - -
- - {/* Rules Display */} - {viewMode === "grid" ? ( - // Grid View (Kart Görünümü) -
- {filteredRules.map((rule) => ( -
-
-
-
- + {/* Rules Display */} + {viewMode === 'grid' ? ( + // Grid View (Kart Görünümü) +
+ {filteredRules.map((rule) => ( +
+
+
+
+ +
+
+

{rule.name}

+

{rule.code}

+
-
-

{rule.name}

-

{rule.code}

-
-
-
- - Öncelik: {rule.priority} - - {rule.isActive ? ( - - ) : ( - - )} -
-
- -
-
-

{rule.description}

-
- - - {getPutawayStrategyText(rule.strategy)} - - - {/* Conditions Summary */} -

- Koşullar ({rule.conditions.length}) -

- {rule.conditions.length > 0 ? ( -
- {rule.conditions.slice(0, 2).map((condition) => ( -
-
- - {getConditionTypeText(condition.conditionType)} - - - {getConditionOperatorText(condition.operator)} - - {condition.value} -
-
- ))} - {rule.conditions.length > 2 && ( -
- +{rule.conditions.length - 2} koşul daha -
+
+ + Öncelik: {rule.priority} + + {rule.isActive ? ( + + ) : ( + )}
- ) : ( -
- Koşul tanımlanmamış -
- )} +
- {/* Target Info */} -

Hedef

-
-
-
-
Depo: {getWarehouseName(rule.warehouseId)}
- {rule.targetZoneId && ( -
Bölge: {getZoneName(rule.targetZoneId)}
- )} - {rule.targetLocationId && ( -
- Lokasyon: {getLocationName(rule.targetLocationId)} +
+
+

{rule.description}

+
+ + + {getPutawayStrategyText(rule.strategy)} + + + {/* Conditions Summary */} +

+ Koşullar ({rule.conditions.length}) +

+ {rule.conditions.length > 0 ? ( +
+ {rule.conditions.slice(0, 2).map((condition) => ( +
+
+ + {getConditionTypeText(condition.conditionType)} + + {getConditionOperatorText(condition.operator)} + {condition.value} +
- )} - {rule.materialTypeId && ( -
- Malzeme Tipi:{" "} - {getMaterialTypeName(rule.materialTypeId)} -
- )} - {rule.materialGroupId && ( -
- Malzeme Grubu:{" "} - {getMaterialGroupName(rule.materialGroupId)} + ))} + {rule.conditions.length > 2 && ( +
+ +{rule.conditions.length - 2} koşul daha
)}
+ ) : ( +
Koşul tanımlanmamış
+ )} + + {/* Target Info */} +

Hedef

+
+
+
+
Depo: {getWarehouseName(rule.warehouseId)}
+ {rule.targetZoneId &&
Bölge: {getZoneName(rule.targetZoneId)}
} + {rule.targetLocationId && ( +
Lokasyon: {getLocationName(rule.targetLocationId)}
+ )} + {rule.materialTypeId && ( +
Malzeme Tipi: {getMaterialTypeName(rule.materialTypeId)}
+ )} + {rule.materialGroupId && ( +
Malzeme Grubu: {getMaterialGroupName(rule.materialGroupId)}
+ )} +
+
+
+
+ +
+
+ {rule.isActive ? ( + <> + + Aktif + + ) : ( + <> + + Pasif + + )} +
+
+ + +
- -
-
- {rule.isActive ? ( - <> - - Aktif - - ) : ( - <> - - Pasif - - )} -
-
- - - -
-
-
- ))} -
- ) : ( - // List View (Liste Görünümü) -
-
- - - - - - - - - - - - - - - {filteredRules.map((rule) => ( - - + + + + + ))} + +
- Kural - - Strateji - - Depo - - Hedef Lokasyon - - Koşullar - - Öncelik - - Durum - - İşlemler -
-
-
- -
-
-
- {rule.name} + ))} +
+ ) : ( + // List View (Liste Görünümü) +
+
+ + + + + + + + + + + + + + + {filteredRules.map((rule) => ( + + - - - - + + + - - + - - - ))} - -
+ Kural + + Strateji + + Depo + + Hedef Lokasyon + + Koşullar + + Öncelik + + Durum + + İşlemler +
+
+
+
-
- {rule.code} -
-
- -
- - {getPutawayStrategyText(rule.strategy)} - - - {getWarehouseName(rule.warehouseId)} - -
- {rule.targetZoneId && ( -
Bölge: {getZoneName(rule.targetZoneId)}
- )} - {rule.targetLocationId && (
- Lokasyon: {getLocationName(rule.targetLocationId)} +
+ {rule.name} +
+
{rule.code}
- )} -
-
- {rule.conditions.length > 0 ? ( + + + + {getPutawayStrategyText(rule.strategy)} + + + {getWarehouseName(rule.warehouseId)} +
- {rule.conditions.slice(0, 1).map((condition) => ( -
- {getConditionTypeText(condition.conditionType)}{" "} - {getConditionOperatorText(condition.operator)}{" "} - {condition.value} -
- ))} - {rule.conditions.length > 1 && ( -
- +{rule.conditions.length - 1} koşul daha -
+ {rule.targetZoneId &&
Bölge: {getZoneName(rule.targetZoneId)}
} + {rule.targetLocationId && ( +
Lokasyon: {getLocationName(rule.targetLocationId)}
)}
- ) : ( - Koşul yok - )} -
- - {rule.priority} - - -
- {rule.isActive ? ( - <> - - - Aktif - - +
+ {rule.conditions.length > 0 ? ( +
+ {rule.conditions.slice(0, 1).map((condition) => ( +
+ {getConditionTypeText(condition.conditionType)}{' '} + {getConditionOperatorText(condition.operator)} {condition.value} +
+ ))} + {rule.conditions.length > 1 && ( +
+ +{rule.conditions.length - 1} koşul daha +
+ )} +
) : ( - <> - - Pasif - + Koşul yok )} - -
-
- - - -
-
+
+ + {rule.priority} + + +
+ {rule.isActive ? ( + <> + + Aktif + + ) : ( + <> + + Pasif + + )} +
+
+
+ + + +
+
+
-
- )} + )} - {filteredRules.length === 0 && ( -
- -

- Kural bulunamadı -

-

- Arama kriterlerinize uygun yerleştirme kuralı bulunamadı. -

- -
- )} + {filteredRules.length === 0 && ( +
+ +

Kural bulunamadı

+

+ Arama kriterlerinize uygun yerleştirme kuralı bulunamadı. +

+ +
+ )} +
{/* Rule Detail Modal */} @@ -717,8 +662,8 @@ const PutawayRules: React.FC = () => {
{ - setShowRuleForm(false); - setEditingRuleId(null); + setShowRuleForm(false) + setEditingRuleId(null) }} /> @@ -726,20 +671,13 @@ const PutawayRules: React.FC = () => {

- {editingRuleId - ? "Yerleştirme Kuralını Güncelle" - : "Yeni Yerleştirme Kuralı"} + {editingRuleId ? 'Yerleştirme Kuralını Güncelle' : 'Yeni Yerleştirme Kuralı'}

- +