Merge remote-tracking branch 'origin/master'
This commit is contained in:
95
scripts/sql/production_product_costing_tbStok_fulltext.sql
Normal file
95
scripts/sql/production_product_costing_tbStok_fulltext.sql
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
Full-text index for Production Product Costing item lookup.
|
||||||
|
Target: SQL Server / URETIM.dbo.tbStok
|
||||||
|
*/
|
||||||
|
|
||||||
|
IF SERVERPROPERTY('IsFullTextInstalled') <> 1
|
||||||
|
BEGIN
|
||||||
|
RAISERROR('SQL Server Full-Text Search yüklü değil.', 16, 1);
|
||||||
|
RETURN;
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF DATABASEPROPERTYEX(DB_NAME(), 'IsFullTextEnabled') <> 1
|
||||||
|
BEGIN
|
||||||
|
RAISERROR('Veritabanında Full-Text Search etkin değil.', 16, 1);
|
||||||
|
RETURN;
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF OBJECT_ID('dbo.tbStok') IS NULL
|
||||||
|
BEGIN
|
||||||
|
RAISERROR('dbo.tbStok bulunamadı.', 16, 1);
|
||||||
|
RETURN;
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys.fulltext_catalogs
|
||||||
|
WHERE name = 'FTC_ProductionProductCosting'
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
CREATE FULLTEXT CATALOG FTC_ProductionProductCosting;
|
||||||
|
END;
|
||||||
|
GO
|
||||||
|
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys.fulltext_indexes
|
||||||
|
WHERE object_id = OBJECT_ID('dbo.tbStok')
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys.fulltext_index_columns fic
|
||||||
|
INNER JOIN sys.columns c
|
||||||
|
ON c.object_id = fic.object_id
|
||||||
|
AND c.column_id = fic.column_id
|
||||||
|
WHERE fic.object_id = OBJECT_ID('dbo.tbStok')
|
||||||
|
AND c.name = 'sAciklama'
|
||||||
|
)
|
||||||
|
BEGIN
|
||||||
|
ALTER FULLTEXT INDEX ON dbo.tbStok
|
||||||
|
ADD (sAciklama LANGUAGE 1055);
|
||||||
|
|
||||||
|
ALTER FULLTEXT INDEX ON dbo.tbStok
|
||||||
|
START FULL POPULATION;
|
||||||
|
END;
|
||||||
|
END;
|
||||||
|
ELSE
|
||||||
|
BEGIN
|
||||||
|
DECLARE @keyIndex sysname;
|
||||||
|
DECLARE @sql nvarchar(max);
|
||||||
|
|
||||||
|
SELECT TOP 1 @keyIndex = i.name
|
||||||
|
FROM sys.indexes i
|
||||||
|
WHERE i.object_id = OBJECT_ID('dbo.tbStok')
|
||||||
|
AND i.is_unique = 1
|
||||||
|
AND i.is_disabled = 0
|
||||||
|
AND i.type IN (1, 2)
|
||||||
|
ORDER BY
|
||||||
|
CASE WHEN i.is_primary_key = 1 THEN 0 ELSE 1 END,
|
||||||
|
i.index_id;
|
||||||
|
|
||||||
|
IF @keyIndex IS NULL
|
||||||
|
BEGIN
|
||||||
|
RAISERROR('dbo.tbStok için uygun unique key index bulunamadı.', 16, 1);
|
||||||
|
RETURN;
|
||||||
|
END;
|
||||||
|
|
||||||
|
SET @sql = N'
|
||||||
|
CREATE FULLTEXT INDEX ON dbo.tbStok
|
||||||
|
(
|
||||||
|
sAciklama LANGUAGE 1055
|
||||||
|
)
|
||||||
|
KEY INDEX ' + QUOTENAME(@keyIndex) + N'
|
||||||
|
ON [FTC_ProductionProductCosting]
|
||||||
|
WITH CHANGE_TRACKING AUTO;';
|
||||||
|
|
||||||
|
EXEC sp_executesql @sql;
|
||||||
|
|
||||||
|
ALTER FULLTEXT INDEX ON dbo.tbStok
|
||||||
|
START FULL POPULATION;
|
||||||
|
END;
|
||||||
|
GO
|
||||||
@@ -23,6 +23,7 @@ UI_DIR=/opt/bssapp/ui/dist
|
|||||||
# ===============================
|
# ===============================
|
||||||
POSTGRES_CONN=host=46.224.33.150 port=5432 user=postgres password=tayitkan dbname=baggib2b sslmode=disable
|
POSTGRES_CONN=host=46.224.33.150 port=5432 user=postgres password=tayitkan dbname=baggib2b sslmode=disable
|
||||||
MSSQL_CONN=sqlserver://sa:Gil_0150@10.0.0.9:1433?database=BAGGI_V3&encrypt=disable
|
MSSQL_CONN=sqlserver://sa:Gil_0150@10.0.0.9:1433?database=BAGGI_V3&encrypt=disable
|
||||||
|
URETIM_MSSQL_CONN=sqlserver://sa:Gil_0150@10.0.0.9:1433?database=URETIM&encrypt=disable
|
||||||
|
|
||||||
# ===============================
|
# ===============================
|
||||||
# PDF
|
# PDF
|
||||||
|
|||||||
0
svc/backend-dev.err.log
Normal file
0
svc/backend-dev.err.log
Normal file
365
svc/backend-dev.out.log
Normal file
365
svc/backend-dev.out.log
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
time=2026-04-29T21:47:12.721+03:00 level=INFO msg="backend start" app=bssapp-backend scope=main
|
||||||
|
time=2026-04-29T21:47:12.767+03:00 level=INFO msg="🔥🔥🔥 BSSAPP BACKEND STARTED — LOGIN ROUTE SHOULD EXIST 🔥🔥🔥" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:12.770+03:00 level=INFO msg="🔐 JWT_SECRET yüklendi" app=bssapp-backend
|
||||||
|
MSSQL baglantisi basarili (connection timeout=120s, dial timeout=120s)
|
||||||
|
URETIM MSSQL baglantisi basarili (connection timeout=120s, dial timeout=120s)
|
||||||
|
time=2026-04-29T21:47:13.271+03:00 level=INFO msg="PostgreSQL bağlantısı başarılı" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:13.581+03:00 level=INFO msg="✅ Admin dept permissions seeded" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:13.581+03:00 level=INFO msg="🟢 auditlog Init called, buffer: 1000" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:13.587+03:00 level=INFO msg="🟢 auditlog worker STARTED" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:13.588+03:00 level=INFO msg="🕵️ AuditLog sistemi başlatıldı (buffer=1000)" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:13.678+03:00 level=INFO msg="[TranslationPerf] index_ready sql=\"CREATE EXTENSION IF NOT EXISTS pg_trgm\"" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:13.765+03:00 level=INFO msg="[TranslationPerf] index_ready sql=\"CREATE INDEX IF NOT EXISTS idx_mk_translator_t_key_lang ON mk_translator (t_key, lang_code)\"" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:13.854+03:00 level=INFO msg="[TranslationPerf] index_ready sql=\"CREATE INDEX IF NOT EXISTS idx_mk_translator_status_lang_updated ON mk_translator (status, lang_code...\"" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:13.944+03:00 level=INFO msg="[TranslationPerf] index_ready sql=\"CREATE INDEX IF NOT EXISTS idx_mk_translator_manual_status ON mk_translator (is_manual, status)\"" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:14.032+03:00 level=INFO msg="[TranslationPerf] index_ready sql=\"CREATE INDEX IF NOT EXISTS idx_mk_translator_source_type_expr ON mk_translator ((COALESCE(NULLIF(pro...\"" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:14.133+03:00 level=INFO msg="[TranslationPerf] index_ready sql=\"CREATE INDEX IF NOT EXISTS idx_mk_translator_source_text_trgm ON mk_translator USING gin (source_tex...\"" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:14.222+03:00 level=INFO msg="[TranslationPerf] index_ready sql=\"CREATE INDEX IF NOT EXISTS idx_mk_translator_translated_text_trgm ON mk_translator USING gin (transl...\"" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:14.223+03:00 level=INFO msg="✉️ Graph Mailer hazır (App-only token) | from=baggiss@baggi.com.tr" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:14.223+03:00 level=INFO msg="✉️ Graph Mailer hazır" app=bssapp-backend
|
||||||
|
📋 [DEBUG] İlk 10 kullanıcı:
|
||||||
|
- 1 : ctengiz
|
||||||
|
- 2 : ali.kale
|
||||||
|
- 5 : mehmet.keçeci
|
||||||
|
- 6 : mert.keçeci
|
||||||
|
- 7 : samet.keçeci
|
||||||
|
- 9 : orhan.caliskan
|
||||||
|
- 10 : nilgun.sara
|
||||||
|
- 14 : rustem.kurbanov
|
||||||
|
- 15 : caner.akyol
|
||||||
|
- 16 : kemal.matyakupov
|
||||||
|
time=2026-04-29T21:47:15.216+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/auth/login [auth:login]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:16.101+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/auth/refresh [auth:refresh]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:16.996+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/password/forgot [auth:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:17.904+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/password/reset/validate/{token} [auth:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:18.782+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/password/reset [auth:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:19.673+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/password/change [auth:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:20.563+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/activity-logs [system:read]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:21.444+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/test-mail [system:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:22.332+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/system/market-mail-mappings/lookups [system:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:23.224+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/system/market-mail-mappings [system:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:24.100+03:00 level=INFO msg="✅ Route+Perm registered → PUT /api/system/market-mail-mappings/{marketId} [system:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:24.978+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/language/translations [language:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:25.870+03:00 level=INFO msg="✅ Route+Perm registered → PUT /api/language/translations/{id} [language:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:26.752+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/language/translations/upsert-missing [language:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:27.638+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/language/translations/sync-sources [language:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:28.520+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/language/translations/translate-selected [language:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:29.449+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/language/translations/bulk-approve [language:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:30.328+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/language/translations/bulk-update [language:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:31.208+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/roles/{id}/permissions [system:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:32.090+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/roles/{id}/permissions [system:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:32.979+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/users/{id}/permissions [system:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:33.884+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/users/{id}/permissions [system:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:34.814+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/permissions/routes [system:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:35.718+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/permissions/effective [system:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:36.597+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/permissions/matrix [system:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:37.490+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/role-dept-permissions/list [system:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:38.395+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/roles/{roleId}/departments/{deptCode}/permissions [system:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:39.290+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/roles/{roleId}/departments/{deptCode}/permissions [system:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:40.177+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/users/list [user:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:41.060+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/users [user:insert]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:41.940+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/users/{id} [user:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:42.841+03:00 level=INFO msg="✅ Route+Perm registered → PUT /api/users/{id} [user:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:43.736+03:00 level=INFO msg="✅ Route+Perm registered → DELETE /api/users/{id} [user:delete]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:44.618+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/users/{id}/admin-reset-password [user:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:45.499+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/users/{id}/send-password-mail [user:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:46.389+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/users/create [user:insert]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:47.287+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/lookups/nebim-users [user:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:48.184+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/lookups/piyasalar [user:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:49.074+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/lookups/users-perm [user:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:49.968+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/lookups/roles-perm [user:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:50.851+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/lookups/departments-perm [user:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:52.990+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/lookups/modules [user:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:53.893+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/lookups/roles [user:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:54.789+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/lookups/departments [user:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:55.694+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/accounts [customer:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:56.587+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/customer-list [customer:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:57.474+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/today-currency [finance:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:58.358+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/export-pdf [finance:export]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:47:59.303+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/exportstamentheaderreport-pdf [finance:export]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:00.197+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/finance/customer-balances [finance:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:01.080+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/finance/customer-balances/export-pdf [finance:export]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:01.968+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/finance/customer-balances/export-excel [finance:export]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:02.850+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/finance/account-aging-statement [finance:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:03.759+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/finance/account-aging-statement/export-pdf [finance:export]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:04.664+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/finance/account-aging-statement/export-screen-pdf [finance:export]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:05.600+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/finance/account-aging-statement/export-excel [finance:export]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:06.513+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/finance/aged-customer-balance-list [finance:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:07.460+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/statements [finance:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:08.375+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/statements/{id}/details [finance:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:09.258+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/order/create [order:insert]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:10.154+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/order/update [order:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:11.034+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/order/{id}/bulk-due-date [order:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:11.924+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/order/get/{id} [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:12.811+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/orders/list [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:13.695+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/orders/production-list [order:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:14.624+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/orders/production-items/cditem-lookups [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:15.544+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/orders/production-items/{id} [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:17.930+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/orders/production-items/{id}/insert-missing [order:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:18.825+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/orders/production-items/{id}/validate [order:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:20.012+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/orders/production-items/{id}/apply [order:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:20.955+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/orders/close-ready [order:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:21.854+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/orders/bulk-close [order:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:23.037+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/orders/export [order:export]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:23.994+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/order/check/{id} [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:24.943+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/order/validate [order:insert]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:26.184+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/order/pdf/{id} [order:export]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:27.066+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/order/send-market-mail [order:read]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:27.952+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/order-inventory [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:28.844+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/orderpricelistb2b [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:29.747+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/min-price [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:30.627+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/products [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:31.512+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-detail [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:32.407+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-cditem [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:33.297+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-colors [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:34.190+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-newcolors [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:35.380+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-colorsize [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:36.265+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-secondcolor [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:37.162+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-newsecondcolor [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:38.051+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-attributes [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:38.944+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-item-attributes [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:39.858+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-stock-query [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:40.753+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-stock-attribute-options [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:41.640+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-stock-query-by-attributes [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:42.531+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-images [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:43.410+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-images/{id}/content [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:44.296+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-size-match/rules [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:45.193+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/products [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:46.071+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/no-cost-products [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:46.982+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/has-cost-products [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:48.168+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/has-cost-history [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:49.052+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/has-cost-detail-groups [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:49.942+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/has-cost-detail-header [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:50.846+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/production-types [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:51.740+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/detail-editor-options [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:52.639+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/has-cost-detail-exchange-rates [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:53.538+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/has-cost-detail-line-history [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:54.426+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/has-cost-detail-similar-history [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:55.308+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/pricing/production-product-costing/has-cost-detail-bulk-prices [order:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:56.205+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/roles [user:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:57.093+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/departments [user:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:48:57.980+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/piyasalar [user:view]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:49:00.122+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/roles/{id}/departments [user:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:49:01.048+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/roles/{id}/piyasalar [user:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:49:01.970+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/users/{id}/roles [user:update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:49:03.170+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/admin/users/{id}/piyasa-sync [admin:user.update]" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:49:03.172+03:00 level=INFO msg="🌍 CORS Allowed Origin: http://ss.baggi.com.tr/app" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:49:03.172+03:00 level=INFO msg="🚀 Server running at: 0.0.0.0:8080" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:49:03.172+03:00 level=INFO msg="🕓 Translation sync next run at 2026-04-30T04:00:00+03:00 (in 6h10m57s)" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:23.596+03:00 level=INFO msg="➡️ POST /api/auth/refresh | auth=false" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:24.523+03:00 level=INFO msg="⬅️ POST /api/auth/refresh | status=200 | 927.7384ms" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:24.524+03:00 level=INFO msg="⚠️ LOGGER: claims is NIL" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:24.524+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=<nil> actor_user=<nil> role=public nav /api/auth/refresh target=<nil>" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:24.544+03:00 level=INFO msg="🔐 GLOBAL AUTH user=5 role=admin" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:24.545+03:00 level=INFO msg="➡️ GET /api/pricing/production-product-costing/has-cost-detail-header | auth=true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:24.546+03:00 level=INFO msg="🔐 GLOBAL AUTH user=5 role=admin" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:24.546+03:00 level=INFO msg="➡️ GET /api/pricing/production-product-costing/has-cost-detail-groups | auth=true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:24.546+03:00 level=INFO msg="AUTH_MIDDLEWARE PASS user=5 role=admin method=GET path=/api/pricing/production-product-costing/has-cost-detail-header" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:24.546+03:00 level=INFO msg="AUTH_MIDDLEWARE PASS user=5 role=admin method=GET path=/api/pricing/production-product-costing/has-cost-detail-groups" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:24.850+03:00 level=INFO msg="🔐 GLOBAL AUTH user=5 role=admin" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:24.850+03:00 level=INFO msg="➡️ GET /api/pricing/production-product-costing/production-types | auth=true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:24.850+03:00 level=INFO msg="AUTH_MIDDLEWARE PASS user=5 role=admin method=GET path=/api/pricing/production-product-costing/production-types" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:24.964+03:00 level=INFO msg="🔐 PERM CHECK user=5 role=3 dept=[UST_YONETIM] order:view" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:24.964+03:00 level=INFO msg="🔐 PERM CHECK user=5 role=3 dept=[UST_YONETIM] order:view" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:25.021+03:00 level=INFO msg="🔐 PERM CHECK user=5 role=3 dept=[UST_YONETIM] order:view" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:25.295+03:00 level=INFO msg=" ↳ ROLE+DEPT OVERRIDE = true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:25.297+03:00 level=INFO msg="request start" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-groups detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:57:25.297+03:00 level=INFO msg="query dispatch" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 query=production-product-costing.no-cost-detail-rows recete_kodu=S001-DMY26211 urun_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:57:25.300+03:00 level=INFO msg=" ↳ ROLE+DEPT OVERRIDE = true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:25.301+03:00 level=INFO msg="request start" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-header detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:57:25.302+03:00 level=INFO msg="query dispatch" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 query=production-product-costing.no-cost-detail-header recete_kodu=S001-DMY26211 urun_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:57:25.350+03:00 level=INFO msg=" ↳ ROLE+DEPT OVERRIDE = true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:25.351+03:00 level=INFO msg="request start" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.production-types
|
||||||
|
time=2026-04-29T21:57:25.389+03:00 level=INFO msg="request done" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-header detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211 n_urt_recete_id=20815 urun_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:57:25.391+03:00 level=INFO msg="⬅️ GET /api/pricing/production-product-costing/has-cost-detail-header | status=200 | 845.3861ms" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:25.391+03:00 level=INFO msg="✅ LOGGER CLAIMS user=mehmet.keçeci role=admin id=5" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:25.391+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=5 actor_user=mehmet.keçeci role=admin nav /api/pricing/production-product-costing/has-cost-detail-header target=<nil>" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:25.406+03:00 level=INFO msg="request done" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.production-types row_count=4
|
||||||
|
time=2026-04-29T21:57:25.406+03:00 level=INFO msg="request done" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-groups detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211 group_count=3 row_count=26 scan_errors=0
|
||||||
|
time=2026-04-29T21:57:25.407+03:00 level=INFO msg="⬅️ GET /api/pricing/production-product-costing/production-types | status=200 | 556.5518ms" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:25.409+03:00 level=INFO msg="✅ LOGGER CLAIMS user=mehmet.keçeci role=admin id=5" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:25.409+03:00 level=INFO msg="[ProductionNoCostDetailGroups] done recete_kodu=S001-DMY26211 groups=3" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:25.411+03:00 level=INFO msg="⬅️ GET /api/pricing/production-product-costing/has-cost-detail-groups | status=200 | 864.8427ms" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:25.412+03:00 level=INFO msg="✅ LOGGER CLAIMS user=mehmet.keçeci role=admin id=5" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:25.567+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=5 actor_user=mehmet.keçeci role=admin nav /api/pricing/production-product-costing/production-types target=<nil>" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:25.733+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=5 actor_user=mehmet.keçeci role=admin nav /api/pricing/production-product-costing/has-cost-detail-groups target=<nil>" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:26.592+03:00 level=INFO msg="🔐 GLOBAL AUTH user=5 role=admin" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:26.593+03:00 level=INFO msg="➡️ GET /api/pricing/production-product-costing/has-cost-detail-exchange-rates | auth=true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:26.593+03:00 level=INFO msg="AUTH_MIDDLEWARE PASS user=5 role=admin method=GET path=/api/pricing/production-product-costing/has-cost-detail-exchange-rates" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:26.758+03:00 level=INFO msg="🔐 PERM CHECK user=5 role=3 dept=[UST_YONETIM] order:view" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:27.095+03:00 level=INFO msg=" ↳ ROLE+DEPT OVERRIDE = true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:27.097+03:00 level=INFO msg="request start" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.exchange-rates maliyet_tarihi=2026-04-29
|
||||||
|
time=2026-04-29T21:57:27.097+03:00 level=INFO msg="[ProductionHasCostDetailExchangeRates] start maliyet_tarihi=2026-04-29" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:27.191+03:00 level=INFO msg="request done" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.exchange-rates rate_date=2026-04-29 usd_rate=45.0515 eur_rate=52.6761 gbp_rate=60.8954
|
||||||
|
time=2026-04-29T21:57:27.191+03:00 level=INFO msg="[ProductionHasCostDetailExchangeRates] done maliyet_tarihi=2026-04-29 rate_date=2026-04-29 usd=45.0515 eur=52.6761" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:27.191+03:00 level=INFO msg="⬅️ GET /api/pricing/production-product-costing/has-cost-detail-exchange-rates | status=200 | 598.5886ms" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:27.191+03:00 level=INFO msg="✅ LOGGER CLAIMS user=mehmet.keçeci role=admin id=5" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:57:27.191+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=5 actor_user=mehmet.keçeci role=admin nav /api/pricing/production-product-costing/has-cost-detail-exchange-rates target=<nil>" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:04.959+03:00 level=INFO msg="🔐 GLOBAL AUTH user=5 role=admin" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:04.961+03:00 level=INFO msg="➡️ GET /api/pricing/production-product-costing/has-cost-detail-header | auth=true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:04.961+03:00 level=INFO msg="🔐 GLOBAL AUTH user=5 role=admin" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:04.961+03:00 level=INFO msg="➡️ GET /api/pricing/production-product-costing/has-cost-detail-groups | auth=true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:04.962+03:00 level=INFO msg="AUTH_MIDDLEWARE PASS user=5 role=admin method=GET path=/api/pricing/production-product-costing/has-cost-detail-header" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:04.962+03:00 level=INFO msg="🔐 PERM CHECK user=5 role=3 dept=[UST_YONETIM] order:view" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:04.963+03:00 level=INFO msg="AUTH_MIDDLEWARE PASS user=5 role=admin method=GET path=/api/pricing/production-product-costing/has-cost-detail-groups" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:04.963+03:00 level=INFO msg="🔐 PERM CHECK user=5 role=3 dept=[UST_YONETIM] order:view" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.269+03:00 level=INFO msg="🔐 GLOBAL AUTH user=5 role=admin" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.270+03:00 level=INFO msg="➡️ GET /api/pricing/production-product-costing/production-types | auth=true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.271+03:00 level=INFO msg="AUTH_MIDDLEWARE PASS user=5 role=admin method=GET path=/api/pricing/production-product-costing/production-types" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.271+03:00 level=INFO msg="🔐 PERM CHECK user=5 role=3 dept=[UST_YONETIM] order:view" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.353+03:00 level=INFO msg=" ↳ ROLE+DEPT OVERRIDE = true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.355+03:00 level=INFO msg="request start" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-groups detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:58:05.355+03:00 level=INFO msg="query dispatch" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 query=production-product-costing.no-cost-detail-rows recete_kodu=S001-DMY26211 urun_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:58:05.394+03:00 level=INFO msg="request done" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-groups detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211 group_count=3 row_count=26 scan_errors=0
|
||||||
|
time=2026-04-29T21:58:05.396+03:00 level=INFO msg="[ProductionNoCostDetailGroups] done recete_kodu=S001-DMY26211 groups=3" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.397+03:00 level=INFO msg="⬅️ GET /api/pricing/production-product-costing/has-cost-detail-groups | status=200 | 435.4404ms" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.397+03:00 level=INFO msg="✅ LOGGER CLAIMS user=mehmet.keçeci role=admin id=5" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.397+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=5 actor_user=mehmet.keçeci role=admin nav /api/pricing/production-product-costing/has-cost-detail-groups target=<nil>" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.432+03:00 level=INFO msg="🔐 GLOBAL AUTH user=5 role=admin" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.433+03:00 level=INFO msg="➡️ GET /api/pricing/production-product-costing/has-cost-detail-groups | auth=true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.433+03:00 level=INFO msg="AUTH_MIDDLEWARE PASS user=5 role=admin method=GET path=/api/pricing/production-product-costing/has-cost-detail-groups" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.433+03:00 level=INFO msg="🔐 PERM CHECK user=5 role=3 dept=[UST_YONETIM] order:view" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.652+03:00 level=INFO msg=" ↳ ROLE+DEPT OVERRIDE = true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.653+03:00 level=INFO msg="request start" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.production-types
|
||||||
|
time=2026-04-29T21:58:05.680+03:00 level=INFO msg="request done" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.production-types row_count=4
|
||||||
|
time=2026-04-29T21:58:05.681+03:00 level=INFO msg="⬅️ GET /api/pricing/production-product-costing/production-types | status=200 | 410.1372ms" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.681+03:00 level=INFO msg="✅ LOGGER CLAIMS user=mehmet.keçeci role=admin id=5" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.681+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=5 actor_user=mehmet.keçeci role=admin nav /api/pricing/production-product-costing/production-types target=<nil>" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.690+03:00 level=INFO msg="🔐 GLOBAL AUTH user=5 role=admin" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.690+03:00 level=INFO msg="➡️ GET /api/pricing/production-product-costing/production-types | auth=true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.691+03:00 level=INFO msg="AUTH_MIDDLEWARE PASS user=5 role=admin method=GET path=/api/pricing/production-product-costing/production-types" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.691+03:00 level=INFO msg="🔐 PERM CHECK user=5 role=3 dept=[UST_YONETIM] order:view" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.943+03:00 level=INFO msg=" ↳ ROLE+DEPT OVERRIDE = true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:05.944+03:00 level=INFO msg="request start" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-header detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:58:05.944+03:00 level=INFO msg="query dispatch" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 query=production-product-costing.no-cost-detail-header recete_kodu=S001-DMY26211 urun_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:58:06.066+03:00 level=INFO msg=" ↳ ROLE+DEPT OVERRIDE = true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:06.067+03:00 level=INFO msg="request start" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.production-types
|
||||||
|
time=2026-04-29T21:58:06.494+03:00 level=INFO msg="request done" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-header detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211 n_urt_recete_id=20815 urun_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:58:06.494+03:00 level=INFO msg="⬅️ GET /api/pricing/production-product-costing/has-cost-detail-header | status=200 | 1.5333479s" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:06.495+03:00 level=INFO msg="✅ LOGGER CLAIMS user=mehmet.keçeci role=admin id=5" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:06.495+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=5 actor_user=mehmet.keçeci role=admin nav /api/pricing/production-product-costing/has-cost-detail-header target=<nil>" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:06.545+03:00 level=INFO msg="request done" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.production-types row_count=4
|
||||||
|
time=2026-04-29T21:58:06.545+03:00 level=INFO msg="⬅️ GET /api/pricing/production-product-costing/production-types | status=200 | 855.4172ms" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:06.546+03:00 level=INFO msg="✅ LOGGER CLAIMS user=mehmet.keçeci role=admin id=5" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:06.667+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=5 actor_user=mehmet.keçeci role=admin nav /api/pricing/production-product-costing/production-types target=<nil>" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:06.751+03:00 level=INFO msg=" ↳ ROLE+DEPT OVERRIDE = true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:06.752+03:00 level=INFO msg="request start" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-groups detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:58:06.753+03:00 level=INFO msg="query dispatch" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 query=production-product-costing.no-cost-detail-rows recete_kodu=S001-DMY26211 urun_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:58:06.817+03:00 level=INFO msg="🔐 GLOBAL AUTH user=5 role=admin" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:06.818+03:00 level=INFO msg="➡️ GET /api/pricing/production-product-costing/has-cost-detail-header | auth=true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:06.818+03:00 level=INFO msg="AUTH_MIDDLEWARE PASS user=5 role=admin method=GET path=/api/pricing/production-product-costing/has-cost-detail-header" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:06.819+03:00 level=INFO msg="🔐 PERM CHECK user=5 role=3 dept=[UST_YONETIM] order:view" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:07.050+03:00 level=INFO msg="request done" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-groups detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211 group_count=3 row_count=26 scan_errors=0
|
||||||
|
time=2026-04-29T21:58:07.050+03:00 level=INFO msg="[ProductionNoCostDetailGroups] done recete_kodu=S001-DMY26211 groups=3" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:07.050+03:00 level=INFO msg="⬅️ GET /api/pricing/production-product-costing/has-cost-detail-groups | status=200 | 1.617361s" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:07.051+03:00 level=INFO msg="✅ LOGGER CLAIMS user=mehmet.keçeci role=admin id=5" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:07.051+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=5 actor_user=mehmet.keçeci role=admin nav /api/pricing/production-product-costing/has-cost-detail-groups target=<nil>" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:07.183+03:00 level=INFO msg=" ↳ ROLE+DEPT OVERRIDE = true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:07.183+03:00 level=INFO msg="request start" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-header detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:58:07.184+03:00 level=INFO msg="query dispatch" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 query=production-product-costing.no-cost-detail-header recete_kodu=S001-DMY26211 urun_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:58:07.213+03:00 level=INFO msg="request done" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-header detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211 n_urt_recete_id=20815 urun_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:58:07.214+03:00 level=INFO msg="⬅️ GET /api/pricing/production-product-costing/has-cost-detail-header | status=200 | 396.483ms" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:07.214+03:00 level=INFO msg="✅ LOGGER CLAIMS user=mehmet.keçeci role=admin id=5" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:07.242+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=5 actor_user=mehmet.keçeci role=admin nav /api/pricing/production-product-costing/has-cost-detail-header target=<nil>" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:08.489+03:00 level=INFO msg="🔐 GLOBAL AUTH user=5 role=admin" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:08.491+03:00 level=INFO msg="➡️ GET /api/pricing/production-product-costing/has-cost-detail-exchange-rates | auth=true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:08.491+03:00 level=INFO msg="AUTH_MIDDLEWARE PASS user=5 role=admin method=GET path=/api/pricing/production-product-costing/has-cost-detail-exchange-rates" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:08.492+03:00 level=INFO msg="🔐 PERM CHECK user=5 role=3 dept=[UST_YONETIM] order:view" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:08.823+03:00 level=INFO msg=" ↳ ROLE+DEPT OVERRIDE = true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:08.823+03:00 level=INFO msg="request start" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.exchange-rates maliyet_tarihi=2026-04-29
|
||||||
|
time=2026-04-29T21:58:08.823+03:00 level=INFO msg="[ProductionHasCostDetailExchangeRates] start maliyet_tarihi=2026-04-29" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:08.851+03:00 level=INFO msg="request done" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.exchange-rates rate_date=2026-04-29 usd_rate=45.0515 eur_rate=52.6761 gbp_rate=60.8954
|
||||||
|
time=2026-04-29T21:58:08.853+03:00 level=INFO msg="[ProductionHasCostDetailExchangeRates] done maliyet_tarihi=2026-04-29 rate_date=2026-04-29 usd=45.0515 eur=52.6761" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:08.853+03:00 level=INFO msg="⬅️ GET /api/pricing/production-product-costing/has-cost-detail-exchange-rates | status=200 | 361.9866ms" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:08.853+03:00 level=INFO msg="✅ LOGGER CLAIMS user=mehmet.keçeci role=admin id=5" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:08.853+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=5 actor_user=mehmet.keçeci role=admin nav /api/pricing/production-product-costing/has-cost-detail-exchange-rates target=<nil>" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:55.938+03:00 level=INFO msg="🔐 GLOBAL AUTH user=5 role=admin" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:55.939+03:00 level=INFO msg="➡️ GET /api/pricing/production-product-costing/has-cost-detail-header | auth=true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:55.939+03:00 level=INFO msg="AUTH_MIDDLEWARE PASS user=5 role=admin method=GET path=/api/pricing/production-product-costing/has-cost-detail-header" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:55.939+03:00 level=INFO msg="🔐 PERM CHECK user=5 role=3 dept=[UST_YONETIM] order:view" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.076+03:00 level=INFO msg="🔐 GLOBAL AUTH user=5 role=admin" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.078+03:00 level=INFO msg="➡️ GET /api/pricing/production-product-costing/has-cost-detail-groups | auth=true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.079+03:00 level=INFO msg="AUTH_MIDDLEWARE PASS user=5 role=admin method=GET path=/api/pricing/production-product-costing/has-cost-detail-groups" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.080+03:00 level=INFO msg="🔐 PERM CHECK user=5 role=3 dept=[UST_YONETIM] order:view" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.251+03:00 level=INFO msg="🔐 GLOBAL AUTH user=5 role=admin" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.252+03:00 level=INFO msg="➡️ GET /api/pricing/production-product-costing/production-types | auth=true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.252+03:00 level=INFO msg="AUTH_MIDDLEWARE PASS user=5 role=admin method=GET path=/api/pricing/production-product-costing/production-types" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.252+03:00 level=INFO msg="🔐 PERM CHECK user=5 role=3 dept=[UST_YONETIM] order:view" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.269+03:00 level=INFO msg=" ↳ ROLE+DEPT OVERRIDE = true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.270+03:00 level=INFO msg="request start" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-header detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:58:56.270+03:00 level=INFO msg="query dispatch" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 query=production-product-costing.no-cost-detail-header recete_kodu=S001-DMY26211 urun_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:58:56.297+03:00 level=INFO msg="request done" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-header detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211 n_urt_recete_id=20815 urun_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:58:56.299+03:00 level=INFO msg="⬅️ GET /api/pricing/production-product-costing/has-cost-detail-header | status=200 | 359.9756ms" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.299+03:00 level=INFO msg="✅ LOGGER CLAIMS user=mehmet.keçeci role=admin id=5" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.299+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=5 actor_user=mehmet.keçeci role=admin nav /api/pricing/production-product-costing/has-cost-detail-header target=<nil>" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.418+03:00 level=INFO msg=" ↳ ROLE+DEPT OVERRIDE = true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.419+03:00 level=INFO msg="request start" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-groups detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:58:56.419+03:00 level=INFO msg="query dispatch" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 query=production-product-costing.no-cost-detail-rows recete_kodu=S001-DMY26211 urun_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:58:56.462+03:00 level=INFO msg="request done" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-groups detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211 group_count=3 row_count=26 scan_errors=0
|
||||||
|
time=2026-04-29T21:58:56.463+03:00 level=INFO msg="[ProductionNoCostDetailGroups] done recete_kodu=S001-DMY26211 groups=3" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.463+03:00 level=INFO msg="⬅️ GET /api/pricing/production-product-costing/has-cost-detail-groups | status=200 | 385.19ms" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.463+03:00 level=INFO msg="✅ LOGGER CLAIMS user=mehmet.keçeci role=admin id=5" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.467+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=5 actor_user=mehmet.keçeci role=admin nav /api/pricing/production-product-costing/has-cost-detail-groups target=<nil>" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.589+03:00 level=INFO msg=" ↳ ROLE+DEPT OVERRIDE = true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.590+03:00 level=INFO msg="request start" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.production-types
|
||||||
|
time=2026-04-29T21:58:56.598+03:00 level=INFO msg="request done" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.production-types row_count=4
|
||||||
|
time=2026-04-29T21:58:56.598+03:00 level=INFO msg="⬅️ GET /api/pricing/production-product-costing/production-types | status=200 | 346.1189ms" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.598+03:00 level=INFO msg="✅ LOGGER CLAIMS user=mehmet.keçeci role=admin id=5" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:56.635+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=5 actor_user=mehmet.keçeci role=admin nav /api/pricing/production-product-costing/production-types target=<nil>" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:57.550+03:00 level=INFO msg="🔐 GLOBAL AUTH user=5 role=admin" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:57.551+03:00 level=INFO msg="➡️ GET /api/pricing/production-product-costing/has-cost-detail-exchange-rates | auth=true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:57.552+03:00 level=INFO msg="AUTH_MIDDLEWARE PASS user=5 role=admin method=GET path=/api/pricing/production-product-costing/has-cost-detail-exchange-rates" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:57.553+03:00 level=INFO msg="🔐 PERM CHECK user=5 role=3 dept=[UST_YONETIM] order:view" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:57.891+03:00 level=INFO msg=" ↳ ROLE+DEPT OVERRIDE = true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:57.891+03:00 level=INFO msg="request start" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.exchange-rates maliyet_tarihi=2026-04-29
|
||||||
|
time=2026-04-29T21:58:57.891+03:00 level=INFO msg="[ProductionHasCostDetailExchangeRates] start maliyet_tarihi=2026-04-29" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:57.923+03:00 level=INFO msg="request done" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.exchange-rates rate_date=2026-04-29 usd_rate=45.0515 eur_rate=52.6761 gbp_rate=60.8954
|
||||||
|
time=2026-04-29T21:58:57.924+03:00 level=INFO msg="[ProductionHasCostDetailExchangeRates] done maliyet_tarihi=2026-04-29 rate_date=2026-04-29 usd=45.0515 eur=52.6761" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:57.925+03:00 level=INFO msg="⬅️ GET /api/pricing/production-product-costing/has-cost-detail-exchange-rates | status=200 | 373.817ms" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:57.925+03:00 level=INFO msg="✅ LOGGER CLAIMS user=mehmet.keçeci role=admin id=5" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:58:57.925+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=5 actor_user=mehmet.keçeci role=admin nav /api/pricing/production-product-costing/has-cost-detail-exchange-rates target=<nil>" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:35.728+03:00 level=INFO msg="🔐 GLOBAL AUTH user=5 role=admin" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:35.729+03:00 level=INFO msg="➡️ GET /api/pricing/production-product-costing/has-cost-detail-header | auth=true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:35.729+03:00 level=INFO msg="🔐 GLOBAL AUTH user=5 role=admin" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:35.730+03:00 level=INFO msg="➡️ GET /api/pricing/production-product-costing/has-cost-detail-groups | auth=true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:35.730+03:00 level=INFO msg="AUTH_MIDDLEWARE PASS user=5 role=admin method=GET path=/api/pricing/production-product-costing/has-cost-detail-groups" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:35.730+03:00 level=INFO msg="🔐 PERM CHECK user=5 role=3 dept=[UST_YONETIM] order:view" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:35.731+03:00 level=INFO msg="AUTH_MIDDLEWARE PASS user=5 role=admin method=GET path=/api/pricing/production-product-costing/has-cost-detail-header" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:35.731+03:00 level=INFO msg="🔐 PERM CHECK user=5 role=3 dept=[UST_YONETIM] order:view" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:35.820+03:00 level=INFO msg="🔐 GLOBAL AUTH user=5 role=admin" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:35.820+03:00 level=INFO msg="➡️ GET /api/pricing/production-product-costing/production-types | auth=true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:35.821+03:00 level=INFO msg="AUTH_MIDDLEWARE PASS user=5 role=admin method=GET path=/api/pricing/production-product-costing/production-types" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:35.821+03:00 level=INFO msg="🔐 PERM CHECK user=5 role=3 dept=[UST_YONETIM] order:view" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:36.078+03:00 level=INFO msg=" ↳ ROLE+DEPT OVERRIDE = true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:36.079+03:00 level=INFO msg="request start" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-header detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:59:36.079+03:00 level=INFO msg="query dispatch" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 query=production-product-costing.no-cost-detail-header recete_kodu=S001-DMY26211 urun_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:59:36.079+03:00 level=INFO msg=" ↳ ROLE+DEPT OVERRIDE = true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:36.079+03:00 level=INFO msg="request start" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-groups detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:59:36.079+03:00 level=INFO msg="query dispatch" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 query=production-product-costing.no-cost-detail-rows recete_kodu=S001-DMY26211 urun_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:59:36.108+03:00 level=INFO msg="request done" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-header detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211 n_urt_recete_id=20815 urun_kodu=S001-DMY26211
|
||||||
|
time=2026-04-29T21:59:36.108+03:00 level=INFO msg="⬅️ GET /api/pricing/production-product-costing/has-cost-detail-header | status=200 | 379.6458ms" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:36.108+03:00 level=INFO msg="✅ LOGGER CLAIMS user=mehmet.keçeci role=admin id=5" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:36.108+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=5 actor_user=mehmet.keçeci role=admin nav /api/pricing/production-product-costing/has-cost-detail-header target=<nil>" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:36.161+03:00 level=INFO msg=" ↳ ROLE+DEPT OVERRIDE = true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:36.161+03:00 level=INFO msg="request start" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.production-types
|
||||||
|
time=2026-04-29T21:59:36.168+03:00 level=INFO msg="request done" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.production-types row_count=4
|
||||||
|
time=2026-04-29T21:59:36.168+03:00 level=INFO msg="⬅️ GET /api/pricing/production-product-costing/production-types | status=200 | 347.978ms" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:36.169+03:00 level=INFO msg="✅ LOGGER CLAIMS user=mehmet.keçeci role=admin id=5" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:36.172+03:00 level=INFO msg="request done" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.detail-groups detail_source=no-cost urun_kodu=S001-DMY26211 recete_kodu=S001-DMY26211 group_count=3 row_count=26 scan_errors=0
|
||||||
|
time=2026-04-29T21:59:36.173+03:00 level=INFO msg="[ProductionNoCostDetailGroups] done recete_kodu=S001-DMY26211 groups=3" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:36.173+03:00 level=INFO msg="⬅️ GET /api/pricing/production-product-costing/has-cost-detail-groups | status=200 | 443.1026ms" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:36.174+03:00 level=INFO msg="✅ LOGGER CLAIMS user=mehmet.keçeci role=admin id=5" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:36.276+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=5 actor_user=mehmet.keçeci role=admin nav /api/pricing/production-product-costing/production-types target=<nil>" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:36.441+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=5 actor_user=mehmet.keçeci role=admin nav /api/pricing/production-product-costing/has-cost-detail-groups target=<nil>" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:37.181+03:00 level=INFO msg="🔐 GLOBAL AUTH user=5 role=admin" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:37.182+03:00 level=INFO msg="➡️ GET /api/pricing/production-product-costing/has-cost-detail-exchange-rates | auth=true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:37.183+03:00 level=INFO msg="AUTH_MIDDLEWARE PASS user=5 role=admin method=GET path=/api/pricing/production-product-costing/has-cost-detail-exchange-rates" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:37.184+03:00 level=INFO msg="🔐 PERM CHECK user=5 role=3 dept=[UST_YONETIM] order:view" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:37.526+03:00 level=INFO msg=" ↳ ROLE+DEPT OVERRIDE = true" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:37.527+03:00 level=INFO msg="request start" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.exchange-rates maliyet_tarihi=2026-04-29
|
||||||
|
time=2026-04-29T21:59:37.527+03:00 level=INFO msg="[ProductionHasCostDetailExchangeRates] start maliyet_tarihi=2026-04-29" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:37.566+03:00 level=INFO msg="request done" app=bssapp-backend trace_id=pcd-no-cost-4d8aee0f-e28c-4f5a-b758-1aea1d646a62 handler=production-product-costing.exchange-rates rate_date=2026-04-29 usd_rate=45.0515 eur_rate=52.6761 gbp_rate=60.8954
|
||||||
|
time=2026-04-29T21:59:37.566+03:00 level=INFO msg="[ProductionHasCostDetailExchangeRates] done maliyet_tarihi=2026-04-29 rate_date=2026-04-29 usd=45.0515 eur=52.6761" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:37.567+03:00 level=INFO msg="⬅️ GET /api/pricing/production-product-costing/has-cost-detail-exchange-rates | status=200 | 384.597ms" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:37.567+03:00 level=INFO msg="✅ LOGGER CLAIMS user=mehmet.keçeci role=admin id=5" app=bssapp-backend
|
||||||
|
time=2026-04-29T21:59:37.568+03:00 level=INFO msg="🧾 auditlog INSERT | actor_dfusr=5 actor_user=mehmet.keçeci role=admin nav /api/pricing/production-product-costing/has-cost-detail-exchange-rates target=<nil>" app=bssapp-backend
|
||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var MssqlDB *sql.DB
|
var MssqlDB *sql.DB
|
||||||
|
var UretimDB *sql.DB
|
||||||
|
|
||||||
func envInt(name string, fallback int) int {
|
func envInt(name string, fallback int) int {
|
||||||
raw := strings.TrimSpace(os.Getenv(name))
|
raw := strings.TrimSpace(os.Getenv(name))
|
||||||
@@ -123,3 +124,37 @@ func ConnectMSSQL() error {
|
|||||||
func GetDB() *sql.DB {
|
func GetDB() *sql.DB {
|
||||||
return MssqlDB
|
return MssqlDB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ConnectMSSQLUretim initializes the URETIM MSSQL connection from environment.
|
||||||
|
func ConnectMSSQLUretim() error {
|
||||||
|
connString := strings.TrimSpace(os.Getenv("URETIM_MSSQL_CONN"))
|
||||||
|
if connString == "" {
|
||||||
|
return fmt.Errorf("URETIM_MSSQL_CONN tanimli degil")
|
||||||
|
}
|
||||||
|
|
||||||
|
connectionTimeoutSec := envInt("URETIM_MSSQL_CONNECTION_TIMEOUT_SEC", envInt("MSSQL_CONNECTION_TIMEOUT_SEC", 120))
|
||||||
|
dialTimeoutSec := envInt("URETIM_MSSQL_DIAL_TIMEOUT_SEC", connectionTimeoutSec)
|
||||||
|
connString = ensureMSSQLTimeouts(connString, connectionTimeoutSec, dialTimeoutSec)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
UretimDB, err = sql.Open("sqlserver", connString)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("URETIM MSSQL baglanti hatasi: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
UretimDB.SetMaxOpenConns(envInt("URETIM_MSSQL_MAX_OPEN_CONNS", envInt("MSSQL_MAX_OPEN_CONNS", 40)))
|
||||||
|
UretimDB.SetMaxIdleConns(envInt("URETIM_MSSQL_MAX_IDLE_CONNS", envInt("MSSQL_MAX_IDLE_CONNS", 40)))
|
||||||
|
UretimDB.SetConnMaxLifetime(time.Duration(envInt("URETIM_MSSQL_CONN_MAX_LIFETIME_MIN", envInt("MSSQL_CONN_MAX_LIFETIME_MIN", 30))) * time.Minute)
|
||||||
|
UretimDB.SetConnMaxIdleTime(time.Duration(envInt("URETIM_MSSQL_CONN_MAX_IDLE_MIN", envInt("MSSQL_CONN_MAX_IDLE_MIN", 10))) * time.Minute)
|
||||||
|
|
||||||
|
if err = UretimDB.Ping(); err != nil {
|
||||||
|
return fmt.Errorf("URETIM MSSQL erisilemiyor: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("URETIM MSSQL baglantisi basarili (connection timeout=%ds, dial timeout=%ds)\n", connectionTimeoutSec, dialTimeoutSec)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetUretimDB() *sql.DB {
|
||||||
|
return UretimDB
|
||||||
|
}
|
||||||
|
|||||||
106
svc/main.go
106
svc/main.go
@@ -8,8 +8,10 @@ import (
|
|||||||
"bssapp-backend/permissions"
|
"bssapp-backend/permissions"
|
||||||
"bssapp-backend/repository"
|
"bssapp-backend/repository"
|
||||||
"bssapp-backend/routes"
|
"bssapp-backend/routes"
|
||||||
|
"bssapp-backend/utils"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"log"
|
"log"
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
@@ -738,6 +740,101 @@ func InitRoutes(pgDB *sql.DB, mssql *sql.DB, ml *mailer.GraphMailer) *mux.Router
|
|||||||
"order", "view",
|
"order", "view",
|
||||||
wrapV3(http.HandlerFunc(routes.GetProductPricingListHandler)),
|
wrapV3(http.HandlerFunc(routes.GetProductPricingListHandler)),
|
||||||
)
|
)
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/pricing/production-product-costing/no-cost-products", "GET",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.GetProductionNoCostProductsHandler)),
|
||||||
|
)
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/pricing/production-product-costing/has-cost-products", "GET",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.GetProductionHasCostProductsHandler)),
|
||||||
|
)
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/pricing/production-product-costing/has-cost-history", "GET",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.GetProductionHasCostHistoryHandler)),
|
||||||
|
)
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/pricing/production-product-costing/has-cost-detail-groups", "GET",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.GetProductionHasCostDetailGroupsHandler)),
|
||||||
|
)
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/pricing/production-product-costing/has-cost-detail-header", "GET",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.GetProductionHasCostDetailHeaderHandler)),
|
||||||
|
)
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/pricing/production-product-costing/production-types", "GET",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.GetProductionTypesHandler)),
|
||||||
|
)
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/pricing/production-product-costing/detail-editor-options", "GET",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.GetProductionHasCostDetailEditorOptionsHandler)),
|
||||||
|
)
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/pricing/production-product-costing/has-cost-detail-exchange-rates", "GET",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.GetProductionHasCostDetailExchangeRatesHandler)),
|
||||||
|
)
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/pricing/production-product-costing/has-cost-detail-line-history", "GET",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.GetProductionHasCostDetailLineHistoryHandler)),
|
||||||
|
)
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/pricing/production-product-costing/has-cost-detail-similar-history", "GET",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.GetProductionHasCostDetailSimilarHistoryHandler)),
|
||||||
|
)
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/pricing/production-product-costing/has-cost-detail-bulk-prices", "POST",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.PostProductionHasCostDetailBulkPricesHandler)),
|
||||||
|
)
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/pricing/production-product-costing/options/urun-ana-grup", "GET",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.GetProductionProductCostingUrunAnaGrupOptionsHandler)),
|
||||||
|
)
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/pricing/production-product-costing/options/urun-alt-grup", "GET",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.GetProductionProductCostingUrunAltGrupOptionsHandler)),
|
||||||
|
)
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/pricing/production-product-costing/options/urun-ana-alt-combos", "GET",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.GetProductionProductCostingUrunAnaAltCombosHandler)),
|
||||||
|
)
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/pricing/production-product-costing/options/mtbolum", "GET",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.GetProductionProductCostingMTBolumOptionsHandler)),
|
||||||
|
)
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/pricing/production-product-costing/maliyet-parca-eslestirme", "GET",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.GetProductionProductCostingParcaMappingsHandler)),
|
||||||
|
)
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/pricing/production-product-costing/maliyet-parca-eslestirme", "DELETE",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.DeleteProductionProductCostingParcaMappingHandler)),
|
||||||
|
)
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/pricing/production-product-costing/maliyet-parca-eslestirme/upsert", "POST",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.PostProductionProductCostingParcaMappingUpsertHandler)),
|
||||||
|
)
|
||||||
|
bindV3(r, pgDB,
|
||||||
|
"/api/pricing/production-product-costing/maliyet-parca-eslestirme/set-active", "POST",
|
||||||
|
"order", "view",
|
||||||
|
wrapV3(http.HandlerFunc(routes.PostProductionProductCostingParcaMappingSetActiveHandler)),
|
||||||
|
)
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// ROLE MANAGEMENT
|
// ROLE MANAGEMENT
|
||||||
@@ -797,6 +894,8 @@ func InitRoutes(pgDB *sql.DB, mssql *sql.DB, ml *mailer.GraphMailer) *mux.Router
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
utils.InitSlog()
|
||||||
|
slog.Info("backend start", "scope", "main")
|
||||||
log.Println("🔥🔥🔥 BSSAPP BACKEND STARTED — LOGIN ROUTE SHOULD EXIST 🔥🔥🔥")
|
log.Println("🔥🔥🔥 BSSAPP BACKEND STARTED — LOGIN ROUTE SHOULD EXIST 🔥🔥🔥")
|
||||||
|
|
||||||
// -------------------------------------------------------
|
// -------------------------------------------------------
|
||||||
@@ -825,6 +924,13 @@ func main() {
|
|||||||
log.Fatal(err3)
|
log.Fatal(err3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if err := db.ConnectMSSQLUretim(); err != nil {
|
||||||
|
if strings.Contains(err.Error(), "URETIM_MSSQL_CONN tanimli degil") {
|
||||||
|
log.Println("⚠️ URETIM DB baglantisi atlandi: URETIM_MSSQL_CONN tanimli degil")
|
||||||
|
} else {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pgDB, err := db.ConnectPostgres()
|
pgDB, err := db.ConnectPostgres()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
282
svc/models/production_product_costing.go
Normal file
282
svc/models/production_product_costing.go
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
type ProductionNoCostProductRow struct {
|
||||||
|
UretimSekli string `json:"UretimSekli"`
|
||||||
|
UrtSiparisNo string `json:"nUrtSiparisNo"`
|
||||||
|
IslemTarihi string `json:"dteIslemTarihi"`
|
||||||
|
FirmaKodu string `json:"FirmaKodu"`
|
||||||
|
FirmaAdi string `json:"FirmaAdi"`
|
||||||
|
SonIsEmriVeren string `json:"SonIsEmriVeren"`
|
||||||
|
ModelAdi string `json:"sAdi"`
|
||||||
|
Kodu string `json:"sKodu"`
|
||||||
|
SKullaniciAdi string `json:"sKullaniciAdi"`
|
||||||
|
SKullaniciGunc string `json:"sKullaniciAdiGunc"`
|
||||||
|
MMiktarG float64 `json:"lMMiktar_G"`
|
||||||
|
MModelKodu string `json:"sMModelKodu"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductionHasCostProductRow struct {
|
||||||
|
UretimSekli string `json:"UretimSekli"`
|
||||||
|
NOnMLNo string `json:"nOnMLNo"`
|
||||||
|
UrunKodu string `json:"UrunKodu"`
|
||||||
|
UrunAdi string `json:"UrunAdi"`
|
||||||
|
Tarihi string `json:"Tarihi"`
|
||||||
|
DteKayitTarihi string `json:"dteKayitTarihi"`
|
||||||
|
SKullaniciAdi string `json:"sKullaniciAdi"`
|
||||||
|
LTutarTL float64 `json:"lTutarTL"`
|
||||||
|
LTutarUSD float64 `json:"lTutarUSD"`
|
||||||
|
LTutarEURO float64 `json:"lTutarEURO"`
|
||||||
|
DteGuncellemeTarihi string `json:"dteGuncellemeTarihi"`
|
||||||
|
SGuncellemeKullaniciAdi string `json:"sGuncellemeKullaniciAdi"`
|
||||||
|
NUrtReceteID string `json:"nUrtReceteID"`
|
||||||
|
SAciklama string `json:"sAciklama"`
|
||||||
|
SonSiparisTarihi string `json:"SonSiparisTarihi"`
|
||||||
|
MaliyetDurumu string `json:"MaliyetDurumu"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductionHasCostHistoryRow struct {
|
||||||
|
NOnMLNo string `json:"nOnMLNo"`
|
||||||
|
UrunKodu string `json:"UrunKodu"`
|
||||||
|
UrunAdi string `json:"UrunAdi"`
|
||||||
|
Tarihi string `json:"Tarihi"`
|
||||||
|
SKullaniciAdi string `json:"sKullaniciAdi"`
|
||||||
|
LTutarUSD float64 `json:"lTutarUSD"`
|
||||||
|
LTutarTL float64 `json:"lTutarTL"`
|
||||||
|
LTutarEURO float64 `json:"lTutarEURO"`
|
||||||
|
LTutarGBP float64 `json:"lTutarGBP"`
|
||||||
|
SDovizCinsi string `json:"sDovizCinsi"`
|
||||||
|
LTutarDoviz float64 `json:"lTutarDoviz"`
|
||||||
|
DteGuncellemeTarihi string `json:"dteGuncellemeTarihi"`
|
||||||
|
SGuncellemeKullaniciAdi string `json:"sGuncellemeKullaniciAdi"`
|
||||||
|
NUrtReceteID string `json:"nUrtReceteID"`
|
||||||
|
SAciklama string `json:"sAciklama"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductionType struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Aciklama string `json:"aciklama"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductionHasCostDetailGroupItem struct {
|
||||||
|
NOnMLNo string `json:"nOnMLNo"`
|
||||||
|
NOnMLDetNo string `json:"nOnMLDetNo"`
|
||||||
|
NHammaddeTuruNo string `json:"nHammaddeTuruNo"`
|
||||||
|
SKodu string `json:"sKodu"`
|
||||||
|
SAciklama string `json:"sAciklama"`
|
||||||
|
SRenk string `json:"sRenk"`
|
||||||
|
SBeden string `json:"sBeden"`
|
||||||
|
SAciklama2 string `json:"sAciklama2"`
|
||||||
|
LMiktar float64 `json:"lMiktar"`
|
||||||
|
LFiyat float64 `json:"lFiyat"`
|
||||||
|
LTutar float64 `json:"lTutar"`
|
||||||
|
SFiyatTipi string `json:"sFiyatTipi"`
|
||||||
|
SDovizCinsi string `json:"sDovizCinsi"`
|
||||||
|
LDovizKuru float64 `json:"lDovizKuru"`
|
||||||
|
LDovizFiyati float64 `json:"lDovizFiyati"`
|
||||||
|
FiyatGirilen *float64 `json:"fiyat_girilen"`
|
||||||
|
FiyatDoviz string `json:"fiyat_doviz"`
|
||||||
|
MaliyeteDahil bool `json:"maliyete_dahil"`
|
||||||
|
CMPriceTypeID *int `json:"cm_price_type_id"`
|
||||||
|
USDTutar float64 `json:"usdTutar"`
|
||||||
|
EURTutar float64 `json:"eurTutar"`
|
||||||
|
GBPTutar float64 `json:"gbpTutar"`
|
||||||
|
SBirim string `json:"sBirim"`
|
||||||
|
SHammaddeTuruAdi string `json:"sHammaddeTuruAdi"`
|
||||||
|
SParcaAdi string `json:"sParcaAdi"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductionHasCostDetailGroup struct {
|
||||||
|
SAciklama3 string `json:"sAciklama3"`
|
||||||
|
TotalTutar float64 `json:"totalTutar"`
|
||||||
|
TotalUSDTutar float64 `json:"totalUSDTutar"`
|
||||||
|
Items []ProductionHasCostDetailGroupItem `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductionHasCostDetailHeader struct {
|
||||||
|
UretimiYapanFirma string `json:"UretimiYapanFirma"`
|
||||||
|
SonIsEmriVeren string `json:"SonIsEmriVeren"`
|
||||||
|
NOnMLNo string `json:"nOnMLNo"`
|
||||||
|
UrunKodu string `json:"UrunKodu"`
|
||||||
|
UrunAdi string `json:"UrunAdi"`
|
||||||
|
UrunAnaGrubu string `json:"UrunAnaGrubu"`
|
||||||
|
UrunAltGrubu string `json:"UrunAltGrubu"`
|
||||||
|
UretimSekliID string `json:"UretimSekliID"`
|
||||||
|
UretimSekli string `json:"UretimSekli"`
|
||||||
|
DteKayitTarihi string `json:"dteKayitTarihi"`
|
||||||
|
SKullaniciAdi string `json:"sKullaniciAdi"`
|
||||||
|
LTutarTL float64 `json:"lTutarTL"`
|
||||||
|
LTutarUSD float64 `json:"lTutarUSD"`
|
||||||
|
LTutarEURO float64 `json:"lTutarEURO"`
|
||||||
|
LTutarGBP float64 `json:"lTutarGBP"`
|
||||||
|
SDovizCinsi string `json:"sDovizCinsi"`
|
||||||
|
LTutarDoviz float64 `json:"lTutarDoviz"`
|
||||||
|
DteGuncellemeTarihi string `json:"dteGuncellemeTarihi"`
|
||||||
|
SGuncellemeKullaniciAdi string `json:"sGuncellemeKullaniciAdi"`
|
||||||
|
NUrtReceteID string `json:"nUrtReceteID"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductionHasCostDetailExchangeRates struct {
|
||||||
|
RateDate string `json:"rateDate"`
|
||||||
|
TRYRate float64 `json:"tryRate"`
|
||||||
|
USDRate float64 `json:"usdRate"`
|
||||||
|
EURRate float64 `json:"eurRate"`
|
||||||
|
GBPRate float64 `json:"gbpRate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductionHasCostDetailEditorOption struct {
|
||||||
|
Kind string `json:"kind"`
|
||||||
|
Value string `json:"value"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
NStokID string `json:"nStokID"`
|
||||||
|
NHammaddeTuruNo string `json:"nHammaddeTuruNo"`
|
||||||
|
SHammaddeTuruAdi string `json:"sHammaddeTuruAdi"`
|
||||||
|
SAciklama3 string `json:"sAciklama3"`
|
||||||
|
SKodu string `json:"sKodu"`
|
||||||
|
SAciklama string `json:"sAciklama"`
|
||||||
|
SModel string `json:"sModel"`
|
||||||
|
SBirim string `json:"sBirim"`
|
||||||
|
ColorCode string `json:"colorCode"`
|
||||||
|
ColorDescription string `json:"colorDescription"`
|
||||||
|
SParcaAdi string `json:"sParcaAdi"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductionHasCostDetailPriceLookupItem struct {
|
||||||
|
RowKey string `json:"__rowKey"`
|
||||||
|
NOnMLNo string `json:"n_onml_no"`
|
||||||
|
NOnMLDetNo string `json:"n_onml_det_no"`
|
||||||
|
NHammaddeTuruNo string `json:"n_hammadde_turu_no"`
|
||||||
|
SKodu string `json:"s_kodu"`
|
||||||
|
SAciklama string `json:"s_aciklama"`
|
||||||
|
SRenk string `json:"s_renk"`
|
||||||
|
ColorCode string `json:"color_code"`
|
||||||
|
ColorDescription string `json:"color_description"`
|
||||||
|
ItemDim1Code string `json:"item_dim1_code"`
|
||||||
|
ItemDim1Description string `json:"item_dim1_description"`
|
||||||
|
SBirim string `json:"s_birim"`
|
||||||
|
LMiktar float64 `json:"l_miktar"`
|
||||||
|
FiyatGirilen float64 `json:"fiyat_girilen"`
|
||||||
|
FiyatDoviz string `json:"fiyat_doviz"`
|
||||||
|
MaliyeteDahil int `json:"maliyete_dahil"`
|
||||||
|
CMPriceTypeID *int `json:"cm_price_type_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductionHasCostDetailBulkPriceRequest struct {
|
||||||
|
NOnMLNo string `json:"n_onml_no"`
|
||||||
|
UrunKodu string `json:"urun_kodu"`
|
||||||
|
NUrtReceteID string `json:"n_urt_recete_id"`
|
||||||
|
MaliyetTarihi string `json:"maliyet_tarihi"`
|
||||||
|
Items []ProductionHasCostDetailPriceLookupItem `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductionHasCostDetailBulkPriceRow struct {
|
||||||
|
RowKey string `json:"__rowKey"`
|
||||||
|
NOnMLDetNo string `json:"nOnMLDetNo"`
|
||||||
|
NHammaddeTuruNo string `json:"nHammaddeTuruNo"`
|
||||||
|
SKodu string `json:"sKodu"`
|
||||||
|
ColorCode string `json:"ColorCode"`
|
||||||
|
ColorDescription string `json:"ColorDescription"`
|
||||||
|
ItemDim1Code string `json:"ItemDim1Code"`
|
||||||
|
ItemDim1Description string `json:"ItemDim1Description"`
|
||||||
|
FiyatGirilen float64 `json:"fiyat_girilen"`
|
||||||
|
FiyatDoviz string `json:"fiyat_doviz"`
|
||||||
|
PriceType string `json:"priceType"`
|
||||||
|
Tarih string `json:"Tarih"`
|
||||||
|
FaturaKodu string `json:"FaturaKodu"`
|
||||||
|
MasrafKodu string `json:"MasrafKodu"`
|
||||||
|
MasrafDetay string `json:"MasrafDetay"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductionHasCostDetailPurchaseHistoryRow struct {
|
||||||
|
SourceType string `json:"sourceType"`
|
||||||
|
Tarih string `json:"Tarih"`
|
||||||
|
FaturaKodu string `json:"FaturaKodu"`
|
||||||
|
FirmaKodu string `json:"FirmaKodu"`
|
||||||
|
FirmaAciklama string `json:"FirmaAciklama"`
|
||||||
|
MasrafKodu string `json:"MasrafKodu"`
|
||||||
|
MasrafDetay string `json:"MasrafDetay"`
|
||||||
|
ColorCode string `json:"ColorCode"`
|
||||||
|
ColorDescription string `json:"ColorDescription"`
|
||||||
|
ItemDim1Code string `json:"ItemDim1Code"`
|
||||||
|
ItemDim1Description string `json:"ItemDim1Description"`
|
||||||
|
Miktar float64 `json:"Miktar"`
|
||||||
|
BIRIM string `json:"BIRIM"`
|
||||||
|
EvrakFiyat float64 `json:"EvrakFiyat"`
|
||||||
|
EvrakTutar float64 `json:"EvrakTutar"`
|
||||||
|
EvrakDoviz string `json:"EvrakDoviz"`
|
||||||
|
PriceType string `json:"priceType"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductionHasCostDetailRecipeHistoryRow struct {
|
||||||
|
SourceType string `json:"sourceType"`
|
||||||
|
DteIslemTarihi string `json:"dteIslemTarihi"`
|
||||||
|
NOnMLNo string `json:"nOnMLNo"`
|
||||||
|
FirmaKodu string `json:"FirmaKodu"`
|
||||||
|
FirmaAciklama string `json:"FirmaAciklama"`
|
||||||
|
SKodu string `json:"sKodu"`
|
||||||
|
SAciklama string `json:"sAciklama"`
|
||||||
|
SRenk string `json:"sRenk"`
|
||||||
|
LMiktar float64 `json:"lMiktar"`
|
||||||
|
SBirim string `json:"sBirim"`
|
||||||
|
LDovizFiyati float64 `json:"lDovizFiyati"`
|
||||||
|
LDovizTutari float64 `json:"lDovizTutari"`
|
||||||
|
USD string `json:"USD"`
|
||||||
|
PriceType string `json:"priceType"`
|
||||||
|
DUMMY string `json:"DUMMY"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductionHasCostDetailLineHistoryResponse struct {
|
||||||
|
PurchaseRows []ProductionHasCostDetailPurchaseHistoryRow `json:"purchaseRows"`
|
||||||
|
RecipeRows []ProductionHasCostDetailRecipeHistoryRow `json:"recipeRows"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductionProductCostingMTBolumMappingRow struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
UrunAnaGrubu string `json:"urunAnaGrubu"`
|
||||||
|
UrunAltGrubu string `json:"urunAltGrubu"`
|
||||||
|
NUrtMTBolumID int `json:"nUrtMTBolumID"`
|
||||||
|
MTBolumAdi string `json:"mtBolumAdi"`
|
||||||
|
BAktif bool `json:"bAktif"`
|
||||||
|
DteIslem string `json:"dteIslemTarihi"`
|
||||||
|
SKullaniciAdi string `json:"sKullaniciAdi"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductionProductCostingMTBolumMappingUpsertRequest struct {
|
||||||
|
UrunAnaGrubu string `json:"urunAnaGrubu"`
|
||||||
|
UrunAltGrubu string `json:"urunAltGrubu"`
|
||||||
|
NUrtMTBolumID int `json:"nUrtMTBolumID"`
|
||||||
|
BAktif bool `json:"bAktif"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// NEW: Maliyet Parca Eslestirme (Ana/Alt Grup + MTBolum + cok secmeli HammaddeTurleri)
|
||||||
|
type ProductionProductCostingParcaMappingRow struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
UrunIlkGrubu string `json:"urunIlkGrubu"`
|
||||||
|
UrunAnaGrubu string `json:"urunAnaGrubu"`
|
||||||
|
UrunAltGrubu string `json:"urunAltGrubu"`
|
||||||
|
NUrtMTBolumID int `json:"nUrtMTBolumID"`
|
||||||
|
ParcaBolumAdi string `json:"parcaBolumAdi"`
|
||||||
|
NHammaddeTurleri []string `json:"nHammaddeTurleri"`
|
||||||
|
BAktif bool `json:"bAktif"`
|
||||||
|
DteIslem string `json:"dteIslemTarihi"`
|
||||||
|
SKullaniciAdi string `json:"sKullaniciAdi"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductionProductCostingParcaMappingUpsertRequest struct {
|
||||||
|
UrunIlkGrubu string `json:"urunIlkGrubu"`
|
||||||
|
UrunAnaGrubu string `json:"urunAnaGrubu"`
|
||||||
|
UrunAltGrubu string `json:"urunAltGrubu"`
|
||||||
|
NUrtMTBolumID int `json:"nUrtMTBolumID"`
|
||||||
|
NHammaddeTurleri []int `json:"nHammaddeTurleri"`
|
||||||
|
BAktif bool `json:"bAktif"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductionProductCostingLookupOption struct {
|
||||||
|
Value string `json:"value"`
|
||||||
|
Label string `json:"label"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProductionProductCostingAnaAltComboRow struct {
|
||||||
|
UrunIlkGrubu string `json:"urunIlkGrubu"`
|
||||||
|
UrunAnaGrubu string `json:"urunAnaGrubu"`
|
||||||
|
UrunAltGrubu string `json:"urunAltGrubu"`
|
||||||
|
}
|
||||||
@@ -121,7 +121,14 @@ LEFT JOIN (
|
|||||||
-- Paketlenen (OrderLine IsClosed=1) belge PB toplam
|
-- Paketlenen (OrderLine IsClosed=1) belge PB toplam
|
||||||
SUM(
|
SUM(
|
||||||
CASE
|
CASE
|
||||||
WHEN ISNULL(l.IsClosed,0) = 1
|
WHEN (
|
||||||
|
ISNULL(l.IsClosed,0) = 1
|
||||||
|
OR EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM dbo.trInvoiceLine il WITH (NOLOCK)
|
||||||
|
WHERE il.OrderLineID = l.OrderLineID
|
||||||
|
)
|
||||||
|
)
|
||||||
AND c.CurrencyCode = h.DocCurrencyCode
|
AND c.CurrencyCode = h.DocCurrencyCode
|
||||||
THEN c.NetAmount
|
THEN c.NetAmount
|
||||||
ELSE 0
|
ELSE 0
|
||||||
@@ -131,7 +138,14 @@ LEFT JOIN (
|
|||||||
-- Paketlenen TRY toplam
|
-- Paketlenen TRY toplam
|
||||||
SUM(
|
SUM(
|
||||||
CASE
|
CASE
|
||||||
WHEN ISNULL(l.IsClosed,0) = 1
|
WHEN (
|
||||||
|
ISNULL(l.IsClosed,0) = 1
|
||||||
|
OR EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM dbo.trInvoiceLine il WITH (NOLOCK)
|
||||||
|
WHERE il.OrderLineID = l.OrderLineID
|
||||||
|
)
|
||||||
|
)
|
||||||
AND c.CurrencyCode = 'TRY'
|
AND c.CurrencyCode = 'TRY'
|
||||||
THEN c.NetAmount
|
THEN c.NetAmount
|
||||||
ELSE 0
|
ELSE 0
|
||||||
|
|||||||
@@ -216,7 +216,16 @@ SELECT
|
|||||||
L.DeliveryDate,
|
L.DeliveryDate,
|
||||||
L.PlannedDateOfLading,
|
L.PlannedDateOfLading,
|
||||||
L.LineDescription,
|
L.LineDescription,
|
||||||
L.IsClosed,
|
CASE
|
||||||
|
WHEN ISNULL(L.IsClosed, 0) = 1
|
||||||
|
OR EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM BAGGI_V3.dbo.trInvoiceLine il WITH (NOLOCK)
|
||||||
|
WHERE il.OrderLineID = L.OrderLineID
|
||||||
|
)
|
||||||
|
THEN CAST(1 AS bit)
|
||||||
|
ELSE CAST(0 AS bit)
|
||||||
|
END AS IsClosed,
|
||||||
L.CreatedUserName,
|
L.CreatedUserName,
|
||||||
L.CreatedDate,
|
L.CreatedDate,
|
||||||
L.LastUpdatedUserName,
|
L.LastUpdatedUserName,
|
||||||
|
|||||||
@@ -143,15 +143,29 @@ JOIN (
|
|||||||
) AS TotalTRY,
|
) AS TotalTRY,
|
||||||
SUM(
|
SUM(
|
||||||
CASE
|
CASE
|
||||||
|
-- "Paketlenen" = satir kapaliysa VEYA satir irsaliyeye/faturaya baglandiysa.
|
||||||
|
-- Not: IsClosed her zaman guncellenmiyor; trInvoiceLine.OrderLineID iliskiyi yakalar.
|
||||||
WHEN ISNULL(l.IsClosed,0) = 1
|
WHEN ISNULL(l.IsClosed,0) = 1
|
||||||
|
OR EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM dbo.trInvoiceLine il WITH (NOLOCK)
|
||||||
|
WHERE il.OrderLineID = l.OrderLineID
|
||||||
|
)
|
||||||
THEN ISNULL(c.NetAmount,0)
|
THEN ISNULL(c.NetAmount,0)
|
||||||
ELSE 0
|
ELSE 0
|
||||||
END
|
END
|
||||||
) AS PackedAmount,
|
) AS PackedAmount,
|
||||||
SUM(
|
SUM(
|
||||||
CASE
|
CASE
|
||||||
WHEN ISNULL(l.IsClosed,0) = 1
|
WHEN (
|
||||||
AND ISNULL(c.CurrencyCode,'') = 'TRY'
|
ISNULL(l.IsClosed,0) = 1
|
||||||
|
OR EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM dbo.trInvoiceLine il WITH (NOLOCK)
|
||||||
|
WHERE il.OrderLineID = l.OrderLineID
|
||||||
|
)
|
||||||
|
)
|
||||||
|
AND ISNULL(c.CurrencyCode,'') = 'TRY'
|
||||||
THEN ISNULL(c.NetAmount,0)
|
THEN ISNULL(c.NetAmount,0)
|
||||||
ELSE 0
|
ELSE 0
|
||||||
END
|
END
|
||||||
|
|||||||
1702
svc/queries/production_product_costing.go
Normal file
1702
svc/queries/production_product_costing.go
Normal file
@@ -0,0 +1,1702 @@
|
|||||||
|
package queries
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bssapp-backend/utils"
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetProductAnaAltGrupByUrunKodu(ctx context.Context, mssqlDB *sql.DB, urunKodu string) (urunAnaGrubu string, urunAltGrubu string, err error) {
|
||||||
|
urunKodu = strings.TrimSpace(urunKodu)
|
||||||
|
if mssqlDB == nil || urunKodu == "" {
|
||||||
|
return "", "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nebim V3: ProductFilterWithDescription exposes ProductAtt01Desc (ana grup) and ProductAtt02Desc (alt grup).
|
||||||
|
sqlText := `
|
||||||
|
SELECT TOP 1
|
||||||
|
ISNULL(ProductAtt01Desc, '') AS UrunAnaGrubu,
|
||||||
|
ISNULL(ProductAtt02Desc, '') AS UrunAltGrubu
|
||||||
|
FROM ProductFilterWithDescription('TR')
|
||||||
|
WHERE IsBlocked = 0
|
||||||
|
AND LTRIM(RTRIM(ProductCode)) = @p1
|
||||||
|
ORDER BY ProductCode;
|
||||||
|
`
|
||||||
|
|
||||||
|
row := mssqlDB.QueryRowContext(ctx, sqlText, urunKodu)
|
||||||
|
if err := row.Scan(&urunAnaGrubu, &urunAltGrubu); err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
return "", "", nil
|
||||||
|
}
|
||||||
|
return "", "", err
|
||||||
|
}
|
||||||
|
return urunAnaGrubu, urunAltGrubu, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionProductCostingAnaGrupOptions(ctx context.Context, mssqlDB *sql.DB, search string, limit int) (*sql.Rows, error) {
|
||||||
|
search = strings.TrimSpace(search)
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 50
|
||||||
|
}
|
||||||
|
searchLike := "%" + search + "%"
|
||||||
|
sqlText := `
|
||||||
|
SELECT TOP (@p2)
|
||||||
|
ISNULL(NULLIF(LTRIM(RTRIM(P.ProductAtt01Desc)), ''), '') AS UrunAnaGrubu
|
||||||
|
FROM ProductFilterWithDescription('TR') AS P
|
||||||
|
WHERE P.IsBlocked = 0
|
||||||
|
AND NULLIF(LTRIM(RTRIM(P.ProductAtt01Desc)), '') IS NOT NULL
|
||||||
|
AND (@p1 = '' OR P.ProductAtt01Desc LIKE @p3)
|
||||||
|
GROUP BY P.ProductAtt01Desc
|
||||||
|
ORDER BY P.ProductAtt01Desc
|
||||||
|
`
|
||||||
|
return mssqlDB.QueryContext(ctx, sqlText, search, limit, searchLike)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionProductCostingAltGrupOptions(ctx context.Context, mssqlDB *sql.DB, urunAnaGrubu string, search string, limit int) (*sql.Rows, error) {
|
||||||
|
urunAnaGrubu = strings.TrimSpace(urunAnaGrubu)
|
||||||
|
search = strings.TrimSpace(search)
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 50
|
||||||
|
}
|
||||||
|
searchLike := "%" + search + "%"
|
||||||
|
sqlText := `
|
||||||
|
SELECT TOP (@p3)
|
||||||
|
ISNULL(NULLIF(LTRIM(RTRIM(P.ProductAtt02Desc)), ''), '') AS UrunAltGrubu
|
||||||
|
FROM ProductFilterWithDescription('TR') AS P
|
||||||
|
WHERE P.IsBlocked = 0
|
||||||
|
AND NULLIF(LTRIM(RTRIM(P.ProductAtt02Desc)), '') IS NOT NULL
|
||||||
|
AND (@p1 = '' OR LTRIM(RTRIM(P.ProductAtt01Desc)) = @p1)
|
||||||
|
AND (@p2 = '' OR P.ProductAtt02Desc LIKE @p4)
|
||||||
|
GROUP BY P.ProductAtt02Desc
|
||||||
|
ORDER BY P.ProductAtt02Desc
|
||||||
|
`
|
||||||
|
return mssqlDB.QueryContext(ctx, sqlText, urunAnaGrubu, search, limit, searchLike)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionProductCostingAnaAltComboRows(ctx context.Context, mssqlDB *sql.DB, search string, limit int) (*sql.Rows, error) {
|
||||||
|
search = strings.TrimSpace(search)
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 500
|
||||||
|
}
|
||||||
|
if limit > 5000 {
|
||||||
|
limit = 5000
|
||||||
|
}
|
||||||
|
// For very short searches, avoid leading-wildcard LIKE which can be extremely expensive on MSSQL side.
|
||||||
|
// Example: "ce" should match "CEKET%" cheaply vs "%ce%" scanning everything.
|
||||||
|
searchLike := "%" + search + "%"
|
||||||
|
if len([]rune(search)) > 0 && len([]rune(search)) < 3 {
|
||||||
|
searchLike = search + "%"
|
||||||
|
}
|
||||||
|
sqlText := `
|
||||||
|
SELECT TOP (@p2)
|
||||||
|
ISNULL(NULLIF(LTRIM(RTRIM(CONVERT(NVARCHAR(200), P.ProductAtt42Desc))), ''), '') AS UrunIlkGrubu,
|
||||||
|
ISNULL(NULLIF(LTRIM(RTRIM(CONVERT(NVARCHAR(200), P.ProductAtt01Desc))), ''), '') AS UrunAnaGrubu,
|
||||||
|
ISNULL(NULLIF(LTRIM(RTRIM(CONVERT(NVARCHAR(200), P.ProductAtt02Desc))), ''), '') AS UrunAltGrubu
|
||||||
|
FROM ProductFilterWithDescription('TR') AS P
|
||||||
|
WHERE P.IsBlocked = 0
|
||||||
|
AND NULLIF(LTRIM(RTRIM(CONVERT(NVARCHAR(200), P.ProductAtt42Desc))), '') IS NOT NULL
|
||||||
|
AND NULLIF(LTRIM(RTRIM(CONVERT(NVARCHAR(200), P.ProductAtt01Desc))), '') IS NOT NULL
|
||||||
|
AND NULLIF(LTRIM(RTRIM(CONVERT(NVARCHAR(200), P.ProductAtt02Desc))), '') IS NOT NULL
|
||||||
|
AND (
|
||||||
|
@p1 = ''
|
||||||
|
OR CONVERT(NVARCHAR(200), P.ProductAtt42Desc) LIKE @p3
|
||||||
|
OR CONVERT(NVARCHAR(200), P.ProductAtt01Desc) LIKE @p3
|
||||||
|
OR CONVERT(NVARCHAR(200), P.ProductAtt02Desc) LIKE @p3
|
||||||
|
)
|
||||||
|
GROUP BY
|
||||||
|
CONVERT(NVARCHAR(200), P.ProductAtt42Desc),
|
||||||
|
CONVERT(NVARCHAR(200), P.ProductAtt01Desc),
|
||||||
|
CONVERT(NVARCHAR(200), P.ProductAtt02Desc)
|
||||||
|
ORDER BY
|
||||||
|
CONVERT(NVARCHAR(200), P.ProductAtt42Desc),
|
||||||
|
CONVERT(NVARCHAR(200), P.ProductAtt02Desc),
|
||||||
|
CONVERT(NVARCHAR(200), P.ProductAtt01Desc)
|
||||||
|
`
|
||||||
|
return mssqlDB.QueryContext(ctx, sqlText, search, limit, searchLike)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionProductCostingMTBolumOptions(ctx context.Context, uretimDB *sql.DB, search string, limit int) (*sql.Rows, error) {
|
||||||
|
search = strings.TrimSpace(search)
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 50
|
||||||
|
}
|
||||||
|
searchLike := "%" + search + "%"
|
||||||
|
sqlText := `
|
||||||
|
SELECT TOP (@p2)
|
||||||
|
ISNULL(B.nUrtMTBolumID, 0) AS nUrtMTBolumID,
|
||||||
|
ISNULL(B.sAdi, '') AS sAdi
|
||||||
|
FROM dbo.spUrtMTBolum B WITH (NOLOCK)
|
||||||
|
WHERE ISNULL(B.bAktif, 0) = 1
|
||||||
|
AND (@p1 = '' OR ISNULL(B.sAdi, '') LIKE @p3 OR CONVERT(VARCHAR(32), ISNULL(B.nUrtMTBolumID, 0)) LIKE @p3)
|
||||||
|
ORDER BY B.nUrtMTBolumID
|
||||||
|
`
|
||||||
|
return uretimDB.QueryContext(ctx, sqlText, search, limit, searchLike)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Maliyet Parca Eslestirme (URETIM DB)
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
func ListProductionProductCostingParcaMappings(ctx context.Context, uretimDB *sql.DB, urunIlkGrubu string, urunAnaGrubu string, urunAltGrubu string, nUrtMTBolumID int, onlyActive *bool) (*sql.Rows, error) {
|
||||||
|
urunIlkGrubu = strings.TrimSpace(urunIlkGrubu)
|
||||||
|
urunAnaGrubu = strings.TrimSpace(urunAnaGrubu)
|
||||||
|
urunAltGrubu = strings.TrimSpace(urunAltGrubu)
|
||||||
|
sqlText := `
|
||||||
|
SELECT
|
||||||
|
M.id,
|
||||||
|
ISNULL(M.UrunIlkGrubu, '') AS UrunIlkGrubu,
|
||||||
|
ISNULL(M.UrunAnaGrubu, '') AS UrunAnaGrubu,
|
||||||
|
ISNULL(M.UrunAltGrubu, '') AS UrunAltGrubu,
|
||||||
|
ISNULL(M.nUrtMTBolumID, 0) AS nUrtMTBolumID,
|
||||||
|
ISNULL(B.sAdi, '') AS MTBolumAdi,
|
||||||
|
ISNULL(H.HammaddeTurleri, '') AS HammaddeTurleri,
|
||||||
|
CAST(CASE WHEN ISNULL(M.bAktif, 0) = 1 THEN 1 ELSE 0 END AS bit) AS bAktif,
|
||||||
|
CONVERT(VARCHAR(16), M.dteIslemTarihi, 120) AS dteIslemTarihi,
|
||||||
|
ISNULL(M.sKullaniciAdi, '') AS sKullaniciAdi
|
||||||
|
FROM dbo.mk_MaliyetParcaEslestirme M WITH (NOLOCK)
|
||||||
|
LEFT JOIN dbo.spUrtMTBolum B WITH (NOLOCK)
|
||||||
|
ON B.nUrtMTBolumID = M.nUrtMTBolumID
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT
|
||||||
|
STUFF((
|
||||||
|
SELECT ',' + LTRIM(RTRIM(CONVERT(VARCHAR(32), X.nHammaddeTuruNo)))
|
||||||
|
FROM dbo.mk_MaliyetParcaEslestirme_HammaddeTuru X WITH (NOLOCK)
|
||||||
|
WHERE X.mapping_id = M.id
|
||||||
|
ORDER BY X.nHammaddeTuruNo
|
||||||
|
FOR XML PATH(''), TYPE
|
||||||
|
).value('.', 'NVARCHAR(MAX)'), 1, 1, '') AS HammaddeTurleri
|
||||||
|
) H
|
||||||
|
WHERE (@p1 = '' OR LTRIM(RTRIM(M.UrunIlkGrubu)) = @p1)
|
||||||
|
AND (@p2 = '' OR LTRIM(RTRIM(M.UrunAnaGrubu)) = @p2)
|
||||||
|
AND (@p3 = '' OR LTRIM(RTRIM(M.UrunAltGrubu)) = @p3)
|
||||||
|
AND (@p4 <= 0 OR M.nUrtMTBolumID = @p4)
|
||||||
|
AND (@p5 IS NULL OR ISNULL(M.bAktif, 0) = @p5)
|
||||||
|
ORDER BY M.UrunAltGrubu, M.UrunAnaGrubu, M.nUrtMTBolumID
|
||||||
|
`
|
||||||
|
var activeParam any = nil
|
||||||
|
if onlyActive != nil {
|
||||||
|
if *onlyActive {
|
||||||
|
activeParam = 1
|
||||||
|
} else {
|
||||||
|
activeParam = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return uretimDB.QueryContext(ctx, sqlText, urunIlkGrubu, urunAnaGrubu, urunAltGrubu, nUrtMTBolumID, activeParam)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpsertProductionProductCostingParcaMapping(ctx context.Context, uretimDB *sql.DB, urunIlkGrubu string, urunAnaGrubu string, urunAltGrubu string, nUrtMTBolumID int, nHammaddeTurleri []int, bAktif bool, user string) (mappingID int, err error) {
|
||||||
|
urunIlkGrubu = strings.TrimSpace(urunIlkGrubu)
|
||||||
|
urunAnaGrubu = strings.TrimSpace(urunAnaGrubu)
|
||||||
|
urunAltGrubu = strings.TrimSpace(urunAltGrubu)
|
||||||
|
user = strings.TrimSpace(user)
|
||||||
|
activeVal := 0
|
||||||
|
if bAktif {
|
||||||
|
activeVal = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := uretimDB.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = tx.Rollback()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 1) Upsert header row and get mapping id
|
||||||
|
sqlText := `
|
||||||
|
DECLARE @id INT;
|
||||||
|
|
||||||
|
SELECT TOP 1 @id = id
|
||||||
|
FROM dbo.mk_MaliyetParcaEslestirme WITH (UPDLOCK, HOLDLOCK)
|
||||||
|
WHERE LTRIM(RTRIM(UrunIlkGrubu)) = @p1
|
||||||
|
AND LTRIM(RTRIM(UrunAnaGrubu)) = @p2
|
||||||
|
AND LTRIM(RTRIM(UrunAltGrubu)) = @p3
|
||||||
|
AND nUrtMTBolumID = @p4;
|
||||||
|
|
||||||
|
IF @id IS NULL
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO dbo.mk_MaliyetParcaEslestirme
|
||||||
|
(UrunIlkGrubu, UrunAnaGrubu, UrunAltGrubu, nUrtMTBolumID, bAktif, sKullaniciAdi, dteIslemTarihi)
|
||||||
|
VALUES
|
||||||
|
(@p1, @p2, @p3, @p4, @p5, NULLIF(@p6, ''), GETDATE());
|
||||||
|
SET @id = SCOPE_IDENTITY();
|
||||||
|
END
|
||||||
|
ELSE
|
||||||
|
BEGIN
|
||||||
|
UPDATE dbo.mk_MaliyetParcaEslestirme
|
||||||
|
SET bAktif = @p5,
|
||||||
|
sKullaniciAdiDeg = NULLIF(@p6, ''),
|
||||||
|
dteIslemTarihiDeg = GETDATE()
|
||||||
|
WHERE id = @id;
|
||||||
|
END
|
||||||
|
|
||||||
|
SELECT @id;
|
||||||
|
`
|
||||||
|
if err = tx.QueryRowContext(ctx, sqlText, urunIlkGrubu, urunAnaGrubu, urunAltGrubu, nUrtMTBolumID, activeVal, user).Scan(&mappingID); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Replace child rows
|
||||||
|
if _, err = tx.ExecContext(ctx, `DELETE FROM dbo.mk_MaliyetParcaEslestirme_HammaddeTuru WHERE mapping_id = @p1;`, mappingID); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
insertChild := `INSERT INTO dbo.mk_MaliyetParcaEslestirme_HammaddeTuru (mapping_id, nHammaddeTuruNo) VALUES (@p1, @p2);`
|
||||||
|
seen := make(map[int]bool, len(nHammaddeTurleri))
|
||||||
|
for _, n := range nHammaddeTurleri {
|
||||||
|
if n <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if seen[n] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[n] = true
|
||||||
|
if _, err = tx.ExecContext(ctx, insertChild, mappingID, n); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = tx.Commit(); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return mappingID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetProductionProductCostingParcaMappingActive(ctx context.Context, uretimDB *sql.DB, id int, bAktif bool, user string) error {
|
||||||
|
user = strings.TrimSpace(user)
|
||||||
|
activeVal := 0
|
||||||
|
if bAktif {
|
||||||
|
activeVal = 1
|
||||||
|
}
|
||||||
|
sqlText := `
|
||||||
|
UPDATE dbo.mk_MaliyetParcaEslestirme
|
||||||
|
SET bAktif = @p2,
|
||||||
|
sKullaniciAdiDeg = NULLIF(@p3, ''),
|
||||||
|
dteIslemTarihiDeg = GETDATE()
|
||||||
|
WHERE id = @p1;
|
||||||
|
`
|
||||||
|
_, err := uretimDB.ExecContext(ctx, sqlText, id, activeVal, user)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteProductionProductCostingParcaMapping(ctx context.Context, uretimDB *sql.DB, id int) error {
|
||||||
|
tx, err := uretimDB.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
_ = tx.Rollback()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if _, err = tx.ExecContext(ctx, `DELETE FROM dbo.mk_MaliyetParcaEslestirme_HammaddeTuru WHERE mapping_id = @p1;`, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err = tx.ExecContext(ctx, `DELETE FROM dbo.mk_MaliyetParcaEslestirme WHERE id = @p1;`, id); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return tx.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionNoCostProducts(
|
||||||
|
ctx context.Context,
|
||||||
|
uretimDB *sql.DB,
|
||||||
|
fromDate string,
|
||||||
|
search string,
|
||||||
|
) (*sql.Rows, error) {
|
||||||
|
fromDate = strings.TrimSpace(fromDate)
|
||||||
|
if fromDate == "" {
|
||||||
|
fromDate = "2025-06-01"
|
||||||
|
}
|
||||||
|
search = strings.TrimSpace(search)
|
||||||
|
|
||||||
|
sqlText := `
|
||||||
|
SELECT
|
||||||
|
CASE
|
||||||
|
WHEN LEFT(LTRIM(RTRIM(R.sMModelKodu)), 1) IN ('N','X','S','O','I','K')
|
||||||
|
THEN N'BAGGI URUN TARAFINDAN URETIME VERILEN'
|
||||||
|
WHEN LEFT(LTRIM(RTRIM(R.sMModelKodu)), 1) IN ('P','F','M')
|
||||||
|
THEN N'FASON ICIN URETILEN'
|
||||||
|
ELSE N'Tanimsiz'
|
||||||
|
END AS UretimSekli,
|
||||||
|
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(SonIsEmri.nUrtSiparisNo, 0))) AS nUrtSiparisNo,
|
||||||
|
CONVERT(VARCHAR(10), SonIsEmri.dteIslemTarihi, 23) AS dteIslemTarihi,
|
||||||
|
ISNULL(SonIsEmri.FirmaKodu, '') AS FirmaKodu,
|
||||||
|
ISNULL(SonIsEmri.FirmaAdi, '') AS FirmaAdi,
|
||||||
|
ISNULL(SonIsEmri.sVeren, '') AS SonIsEmriVeren,
|
||||||
|
ISNULL(SonIsEmri.lMMiktar_G, 0) AS lMMiktar_G,
|
||||||
|
LTRIM(RTRIM(R.sMModelKodu)) AS sMModelKodu,
|
||||||
|
ISNULL(R.sKodu, '') AS sKodu,
|
||||||
|
ISNULL(R.sAdi, '') AS sAdi,
|
||||||
|
ISNULL(R.sKullaniciAdi, '') AS sKullaniciAdi,
|
||||||
|
ISNULL(R.sKullaniciAdiGunc, '') AS sKullaniciAdiGunc
|
||||||
|
|
||||||
|
FROM dbo.spUrtRecete R
|
||||||
|
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1
|
||||||
|
SD.nUrtSiparisID,
|
||||||
|
SM.nUrtSiparisNo,
|
||||||
|
SD.dteIslemTarihi,
|
||||||
|
SD.lMMiktar_G,
|
||||||
|
SM.sVeren,
|
||||||
|
F.nFirmaID,
|
||||||
|
F.sKodu AS FirmaKodu,
|
||||||
|
F.sAciklama AS FirmaAdi
|
||||||
|
FROM dbo.spUrtSiparisDet SD
|
||||||
|
INNER JOIN dbo.spUrtSiparis SM
|
||||||
|
ON SM.nUrtSiparisID = SD.nUrtSiparisID
|
||||||
|
LEFT JOIN dbo.tbFirma F
|
||||||
|
ON F.nFirmaID = SM.nFirmaID
|
||||||
|
WHERE SD.nUrtReceteID = R.nUrtReceteID
|
||||||
|
ORDER BY SD.dteIslemTarihi DESC, SD.nUrtSiparisID DESC
|
||||||
|
) SonIsEmri
|
||||||
|
|
||||||
|
WHERE LTRIM(RTRIM(R.sMModelKodu)) <> ''
|
||||||
|
AND LEN(LTRIM(RTRIM(R.sMModelKodu))) = 13
|
||||||
|
AND R.dteIslemTarihi > @p1
|
||||||
|
AND (
|
||||||
|
@p2 = ''
|
||||||
|
OR LTRIM(RTRIM(R.sMModelKodu)) LIKE '%' + @p2 + '%'
|
||||||
|
OR ISNULL(SonIsEmri.FirmaKodu, '') LIKE '%' + @p2 + '%'
|
||||||
|
OR ISNULL(SonIsEmri.FirmaAdi, '') LIKE '%' + @p2 + '%'
|
||||||
|
OR ISNULL(SonIsEmri.sVeren, '') LIKE '%' + @p2 + '%'
|
||||||
|
)
|
||||||
|
AND NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM dbo.spUrtOnMLMas M
|
||||||
|
WHERE LTRIM(RTRIM(M.UrunKodu)) = LTRIM(RTRIM(R.sMModelKodu))
|
||||||
|
)
|
||||||
|
ORDER BY SonIsEmri.dteIslemTarihi DESC, LTRIM(RTRIM(R.sMModelKodu)) ASC
|
||||||
|
`
|
||||||
|
|
||||||
|
return uretimDB.QueryContext(ctx, sqlText, fromDate, search)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionHasCostProducts(
|
||||||
|
ctx context.Context,
|
||||||
|
uretimDB *sql.DB,
|
||||||
|
search string,
|
||||||
|
offset int,
|
||||||
|
limit int,
|
||||||
|
) (*sql.Rows, error) {
|
||||||
|
search = strings.TrimSpace(search)
|
||||||
|
|
||||||
|
sqlText := `
|
||||||
|
WITH SonOnMaliyet AS (
|
||||||
|
SELECT
|
||||||
|
M.*,
|
||||||
|
ROW_NUMBER() OVER (
|
||||||
|
PARTITION BY LTRIM(RTRIM(M.UrunKodu))
|
||||||
|
ORDER BY
|
||||||
|
M.Tarihi DESC,
|
||||||
|
M.dteGuncellemeTarihi DESC,
|
||||||
|
M.nOnMLNo DESC
|
||||||
|
) AS rn
|
||||||
|
FROM dbo.spUrtOnMLMas M
|
||||||
|
WHERE LTRIM(RTRIM(M.UrunKodu)) <> ''
|
||||||
|
),
|
||||||
|
SonSiparis AS (
|
||||||
|
SELECT
|
||||||
|
LTRIM(RTRIM(sMModelKodu)) AS UrunKodu,
|
||||||
|
MAX(dteIslemTarihi) AS SonSiparisTarihi
|
||||||
|
FROM dbo.spUrtSiparisDet
|
||||||
|
GROUP BY LTRIM(RTRIM(sMModelKodu))
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
CASE
|
||||||
|
WHEN LEFT(LTRIM(RTRIM(OM.UrunKodu)), 1) IN ('N','X','S','O','I','K')
|
||||||
|
THEN N'BAGGI URUN TARAFINDAN URETIME VERILEN'
|
||||||
|
WHEN LEFT(LTRIM(RTRIM(OM.UrunKodu)), 1) IN ('P','F','M')
|
||||||
|
THEN N'FASON ICIN URETILEN'
|
||||||
|
ELSE N'Tanimsiz'
|
||||||
|
END AS UretimSekli,
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(OM.nOnMLNo, 0))) AS nOnMLNo,
|
||||||
|
LTRIM(RTRIM(ISNULL(OM.UrunKodu, ''))) AS UrunKodu,
|
||||||
|
ISNULL(OM.UrunAdi, '') AS UrunAdi,
|
||||||
|
CONVERT(VARCHAR(10), OM.Tarihi, 23) AS Tarihi,
|
||||||
|
CONVERT(VARCHAR(10), OM.dteKayitTarihi, 23) AS dteKayitTarihi,
|
||||||
|
ISNULL(OM.sKullaniciAdi, '') AS sKullaniciAdi,
|
||||||
|
ISNULL(OM.lTutarTL, 0) AS lTutarTL,
|
||||||
|
ISNULL(OM.lTutarUSD, 0) AS lTutarUSD,
|
||||||
|
ISNULL(OM.lTutarEURO, 0) AS lTutarEURO,
|
||||||
|
CONVERT(VARCHAR(10), OM.dteGuncellemeTarihi, 23) AS dteGuncellemeTarihi,
|
||||||
|
ISNULL(OM.sGuncellemeKullaniciAdi, '') AS sGuncellemeKullaniciAdi,
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(OM.nUrtReceteID, 0))) AS nUrtReceteID,
|
||||||
|
ISNULL(OM.sAciklama, '') AS sAciklama,
|
||||||
|
ISNULL(CONVERT(VARCHAR(10), SS.SonSiparisTarihi, 23), '') AS SonSiparisTarihi,
|
||||||
|
CASE
|
||||||
|
WHEN ISNULL(OM.lTutarUSD, 0) = 0 THEN N'GUNCELLEME GEREKIYOR'
|
||||||
|
WHEN SS.SonSiparisTarihi IS NULL THEN N'SIPARIS YOK'
|
||||||
|
WHEN SS.SonSiparisTarihi > OM.Tarihi THEN N'GUNCELLEME GEREKIYOR'
|
||||||
|
ELSE N'GUNCEL'
|
||||||
|
END AS MaliyetDurumu
|
||||||
|
FROM SonOnMaliyet OM
|
||||||
|
LEFT JOIN SonSiparis SS
|
||||||
|
ON SS.UrunKodu = LTRIM(RTRIM(OM.UrunKodu))
|
||||||
|
WHERE OM.rn = 1
|
||||||
|
AND (
|
||||||
|
@p1 = ''
|
||||||
|
OR RTRIM(CONVERT(VARCHAR(32), ISNULL(OM.nOnMLNo, 0))) LIKE '%' + @p1 + '%'
|
||||||
|
OR LTRIM(RTRIM(OM.UrunKodu)) LIKE '%' + @p1 + '%'
|
||||||
|
OR ISNULL(OM.UrunAdi, '') LIKE '%' + @p1 + '%'
|
||||||
|
OR ISNULL(OM.sAciklama, '') LIKE '%' + @p1 + '%'
|
||||||
|
)
|
||||||
|
ORDER BY
|
||||||
|
SS.SonSiparisTarihi DESC,
|
||||||
|
OM.nOnMLNo DESC
|
||||||
|
OFFSET @p2 ROWS
|
||||||
|
FETCH NEXT @p3 ROWS ONLY
|
||||||
|
`
|
||||||
|
|
||||||
|
return uretimDB.QueryContext(ctx, sqlText, search, offset, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionHasCostHistoryByProductCode(
|
||||||
|
ctx context.Context,
|
||||||
|
uretimDB *sql.DB,
|
||||||
|
productCode string,
|
||||||
|
) (*sql.Rows, error) {
|
||||||
|
productCode = strings.TrimSpace(productCode)
|
||||||
|
|
||||||
|
sqlText := `
|
||||||
|
SELECT
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(M.nOnMLNo, 0))) AS nOnMLNo,
|
||||||
|
LTRIM(RTRIM(ISNULL(M.UrunKodu, ''))) AS UrunKodu,
|
||||||
|
ISNULL(M.UrunAdi, '') AS UrunAdi,
|
||||||
|
CONVERT(VARCHAR(16), M.Tarihi, 120) AS Tarihi,
|
||||||
|
ISNULL(M.sKullaniciAdi, '') AS sKullaniciAdi,
|
||||||
|
ISNULL(M.lTutarUSD, 0) AS lTutarUSD,
|
||||||
|
ISNULL(M.lTutarTL, 0) AS lTutarTL,
|
||||||
|
ISNULL(M.lTutarEURO, 0) AS lTutarEURO,
|
||||||
|
ISNULL(M.sDovizCinsi, '') AS sDovizCinsi,
|
||||||
|
ISNULL(M.lTutarDoviz, 0) AS lTutarDoviz,
|
||||||
|
CONVERT(VARCHAR(16), M.dteGuncellemeTarihi, 120) AS dteGuncellemeTarihi,
|
||||||
|
ISNULL(M.sGuncellemeKullaniciAdi, '') AS sGuncellemeKullaniciAdi,
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(M.nUrtReceteID, 0))) AS nUrtReceteID,
|
||||||
|
ISNULL(M.sAciklama, '') AS sAciklama
|
||||||
|
FROM dbo.spUrtOnMLMas M
|
||||||
|
WHERE LTRIM(RTRIM(M.UrunKodu)) = @p1
|
||||||
|
ORDER BY
|
||||||
|
M.Tarihi DESC,
|
||||||
|
M.dteGuncellemeTarihi DESC,
|
||||||
|
M.nOnMLNo DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
return uretimDB.QueryContext(ctx, sqlText, productCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionHasCostDetailRowsByOnMLNo(
|
||||||
|
ctx context.Context,
|
||||||
|
uretimDB *sql.DB,
|
||||||
|
nOnMLNo int,
|
||||||
|
) (*sql.Rows, error) {
|
||||||
|
sqlText := `
|
||||||
|
SELECT
|
||||||
|
ISNULL(NULLIF(LTRIM(RTRIM(T.sAciklama3)), ''), N'TANIMSIZ') AS sAciklama3,
|
||||||
|
SUM(ISNULL(D.lTutar, 0)) OVER (
|
||||||
|
PARTITION BY ISNULL(NULLIF(LTRIM(RTRIM(T.sAciklama3)), ''), N'TANIMSIZ')
|
||||||
|
) AS GroupTotalTutar,
|
||||||
|
SUM(ISNULL(D.lMiktar, 0) * ISNULL(D.lDovizFiyati, 0)) OVER (
|
||||||
|
PARTITION BY ISNULL(NULLIF(LTRIM(RTRIM(T.sAciklama3)), ''), N'TANIMSIZ')
|
||||||
|
) AS GroupTotalUSDTutar,
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(D.nOnMLNo, 0))) AS nOnMLNo,
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(D.nOnMLDetNo, 0))) AS nOnMLDetNo,
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(D.nHammaddeTuruNo, 0))) AS nHammaddeTuruNo,
|
||||||
|
ISNULL(D.sKodu, '') AS sKodu,
|
||||||
|
ISNULL(D.sAciklama, '') AS sAciklama,
|
||||||
|
ISNULL(D.sRenk, '') AS sRenk,
|
||||||
|
ISNULL(D.sBeden, '') AS sBeden,
|
||||||
|
ISNULL(D.sAciklama2, '') AS sAciklama2,
|
||||||
|
ISNULL(D.lMiktar, 0) AS lMiktar,
|
||||||
|
ISNULL(D.lFiyat, 0) AS lFiyat,
|
||||||
|
ISNULL(D.lTutar, 0) AS lTutar,
|
||||||
|
ISNULL(D.sFiyatTipi, '') AS sFiyatTipi,
|
||||||
|
ISNULL(D.sDovizCinsi, '') AS sDovizCinsi,
|
||||||
|
ISNULL(D.lDovizKuru, 0) AS lDovizKuru,
|
||||||
|
ISNULL(D.lDovizFiyati, 0) AS lDovizFiyati,
|
||||||
|
D.fiyat_girilen AS fiyat_girilen,
|
||||||
|
D.fiyat_doviz AS fiyat_doviz,
|
||||||
|
CAST(CASE WHEN ISNULL(D.Maliyete_dahil, 0) = 1 THEN 1 ELSE 0 END AS bit) AS maliyete_dahil,
|
||||||
|
D.cm_price_type_id AS cm_price_type_id,
|
||||||
|
ISNULL(D.lMiktar, 0) * ISNULL(D.lDovizFiyati, 0) AS usdTutar,
|
||||||
|
0.0 AS eurTutar,
|
||||||
|
0.0 AS gbpTutar,
|
||||||
|
ISNULL(D.sBirim, '') AS sBirim,
|
||||||
|
ISNULL(T.sAciklama, '') AS sHammaddeTuruAdi,
|
||||||
|
ISNULL(B.sAdi, '') AS sParcaAdi
|
||||||
|
FROM dbo.spUrtOnMLMasDet D
|
||||||
|
LEFT JOIN dbo.spUrtOnMLHammaddeTuru T
|
||||||
|
ON T.nHammaddeTuruNo = D.nHammaddeTuruNo
|
||||||
|
LEFT JOIN dbo.spUrtMTBolum B
|
||||||
|
ON B.nUrtMTBolumID = D.nUrtMTBolumID
|
||||||
|
WHERE D.nOnMLNo = @p1
|
||||||
|
ORDER BY
|
||||||
|
GroupTotalTutar DESC,
|
||||||
|
sAciklama3 ASC,
|
||||||
|
ISNULL(D.lTutar, 0) DESC,
|
||||||
|
D.nOnMLDetNo ASC
|
||||||
|
`
|
||||||
|
|
||||||
|
return uretimDB.QueryContext(ctx, sqlText, nOnMLNo)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionHasCostDetailHeaderByOnMLNo(
|
||||||
|
ctx context.Context,
|
||||||
|
uretimDB *sql.DB,
|
||||||
|
nOnMLNo int,
|
||||||
|
) (*sql.Row, error) {
|
||||||
|
sqlText := `
|
||||||
|
SELECT TOP 1
|
||||||
|
ISNULL(UF.UretimiYapanFirma, '') AS UretimiYapanFirma,
|
||||||
|
ISNULL(UF.SonIsEmriVeren, '') AS SonIsEmriVeren,
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(M.nOnMLNo, 0))) AS nOnMLNo,
|
||||||
|
LTRIM(RTRIM(ISNULL(M.UrunKodu, ''))) AS UrunKodu,
|
||||||
|
ISNULL(M.UrunAdi, '') AS UrunAdi,
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(M.uretim_sekli_id, 0))) AS uretim_sekli_id,
|
||||||
|
ISNULL(US.aciklama, '') AS uretim_sekli,
|
||||||
|
CONVERT(VARCHAR(16), M.dteKayitTarihi, 120) AS dteKayitTarihi,
|
||||||
|
ISNULL(M.sKullaniciAdi, '') AS sKullaniciAdi,
|
||||||
|
ISNULL(M.lTutarTL, 0) AS lTutarTL,
|
||||||
|
ISNULL(M.lTutarUSD, 0) AS lTutarUSD,
|
||||||
|
ISNULL(M.lTutarEURO, 0) AS lTutarEURO,
|
||||||
|
0.0 AS lTutarGBP,
|
||||||
|
ISNULL(M.sDovizCinsi, '') AS sDovizCinsi,
|
||||||
|
ISNULL(M.lTutarDoviz, 0) AS lTutarDoviz,
|
||||||
|
CONVERT(VARCHAR(16), M.dteGuncellemeTarihi, 120) AS dteGuncellemeTarihi,
|
||||||
|
ISNULL(M.sGuncellemeKullaniciAdi, '') AS sGuncellemeKullaniciAdi,
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(M.nUrtReceteID, 0))) AS nUrtReceteID
|
||||||
|
FROM dbo.spUrtOnMLMas M
|
||||||
|
LEFT JOIN dbo.mk_uretim_sekli US
|
||||||
|
ON US.id = M.uretim_sekli_id
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1
|
||||||
|
ISNULL(F.sAciklama, '') AS UretimiYapanFirma,
|
||||||
|
ISNULL(SM.sVeren, '') AS SonIsEmriVeren
|
||||||
|
FROM dbo.spUrtSiparisDet SD
|
||||||
|
INNER JOIN dbo.spUrtSiparis SM
|
||||||
|
ON SM.nUrtSiparisID = SD.nUrtSiparisID
|
||||||
|
LEFT JOIN dbo.tbFirma F
|
||||||
|
ON F.nFirmaID = SM.nFirmaID
|
||||||
|
WHERE LTRIM(RTRIM(SD.sMModelKodu)) = LTRIM(RTRIM(M.UrunKodu))
|
||||||
|
ORDER BY SD.dteIslemTarihi DESC, SD.nUrtSiparisID DESC
|
||||||
|
) UF
|
||||||
|
WHERE M.nOnMLNo = @p1
|
||||||
|
ORDER BY M.nOnMLNo DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
return uretimDB.QueryRowContext(ctx, sqlText, nOnMLNo), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionNoCostDetailHeaderByRecipeCode(
|
||||||
|
ctx context.Context,
|
||||||
|
uretimDB *sql.DB,
|
||||||
|
recipeCode string,
|
||||||
|
productCode string,
|
||||||
|
) (*sql.Row, error) {
|
||||||
|
recipeCode = strings.TrimSpace(recipeCode)
|
||||||
|
productCode = strings.TrimSpace(productCode)
|
||||||
|
utils.SlogFromContext(ctx).With(
|
||||||
|
"query", "production-product-costing.no-cost-detail-header",
|
||||||
|
"recete_kodu", recipeCode,
|
||||||
|
"urun_kodu", productCode,
|
||||||
|
).Info("query dispatch")
|
||||||
|
|
||||||
|
sqlText := `
|
||||||
|
WITH RecipeMatch AS (
|
||||||
|
SELECT TOP 1
|
||||||
|
R.nUrtReceteID,
|
||||||
|
LTRIM(RTRIM(ISNULL(R.sMModelKodu, ''))) AS UrunKodu,
|
||||||
|
ISNULL(R.sAdi, '') AS UrunAdi,
|
||||||
|
CONVERT(VARCHAR(16), R.dteIslemTarihi, 120) AS dteKayitTarihi,
|
||||||
|
ISNULL(R.sKullaniciAdi, '') AS sKullaniciAdi,
|
||||||
|
ISNULL(R.sKullaniciAdiGunc, '') AS sGuncellemeKullaniciAdi,
|
||||||
|
CASE
|
||||||
|
WHEN LEFT(LTRIM(RTRIM(R.sMModelKodu)), 1) IN ('N','X','S','O','I','K')
|
||||||
|
THEN N'BAGGI URUN TARAFINDAN URETIME VERILEN'
|
||||||
|
WHEN LEFT(LTRIM(RTRIM(R.sMModelKodu)), 1) IN ('P','F','M')
|
||||||
|
THEN N'FASON ICIN URETILEN'
|
||||||
|
ELSE N'Tanimsiz'
|
||||||
|
END AS UretimSekli
|
||||||
|
FROM dbo.spUrtRecete R
|
||||||
|
WHERE LTRIM(RTRIM(ISNULL(R.sKodu, ''))) = @p1
|
||||||
|
AND (@p2 = '' OR LTRIM(RTRIM(ISNULL(R.sMModelKodu, ''))) = @p2)
|
||||||
|
ORDER BY
|
||||||
|
R.dteIslemTarihi DESC,
|
||||||
|
R.nUrtReceteID DESC
|
||||||
|
)
|
||||||
|
SELECT TOP 1
|
||||||
|
ISNULL(SonIsEmri.FirmaAdi, '') AS UretimiYapanFirma,
|
||||||
|
ISNULL(SonIsEmri.SonIsEmriVeren, '') AS SonIsEmriVeren,
|
||||||
|
'' AS nOnMLNo,
|
||||||
|
RM.UrunKodu,
|
||||||
|
RM.UrunAdi,
|
||||||
|
'' AS UretimSekliID,
|
||||||
|
RM.UretimSekli,
|
||||||
|
ISNULL(SonIsEmri.dteIslemTarihi, RM.dteKayitTarihi) AS dteKayitTarihi,
|
||||||
|
RM.sKullaniciAdi,
|
||||||
|
0.0 AS lTutarTL,
|
||||||
|
0.0 AS lTutarUSD,
|
||||||
|
0.0 AS lTutarEURO,
|
||||||
|
0.0 AS lTutarGBP,
|
||||||
|
'USD' AS sDovizCinsi,
|
||||||
|
0.0 AS lTutarDoviz,
|
||||||
|
'' AS dteGuncellemeTarihi,
|
||||||
|
RM.sGuncellemeKullaniciAdi,
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(RM.nUrtReceteID, 0))) AS nUrtReceteID
|
||||||
|
FROM RecipeMatch RM
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1
|
||||||
|
ISNULL(F.sAciklama, '') AS FirmaAdi,
|
||||||
|
ISNULL(SM.sVeren, '') AS SonIsEmriVeren,
|
||||||
|
CONVERT(VARCHAR(16), SD.dteIslemTarihi, 120) AS dteIslemTarihi
|
||||||
|
FROM dbo.spUrtSiparisDet SD
|
||||||
|
INNER JOIN dbo.spUrtSiparis SM
|
||||||
|
ON SM.nUrtSiparisID = SD.nUrtSiparisID
|
||||||
|
LEFT JOIN dbo.tbFirma F
|
||||||
|
ON F.nFirmaID = SM.nFirmaID
|
||||||
|
WHERE SD.nUrtReceteID = RM.nUrtReceteID
|
||||||
|
ORDER BY SD.dteIslemTarihi DESC, SD.nUrtSiparisID DESC
|
||||||
|
) SonIsEmri
|
||||||
|
`
|
||||||
|
|
||||||
|
return uretimDB.QueryRowContext(ctx, sqlText, recipeCode, productCode), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionNoCostDetailRowsByRecipeCode(
|
||||||
|
ctx context.Context,
|
||||||
|
uretimDB *sql.DB,
|
||||||
|
recipeCode string,
|
||||||
|
productCode string,
|
||||||
|
) (*sql.Rows, error) {
|
||||||
|
recipeCode = strings.TrimSpace(recipeCode)
|
||||||
|
productCode = strings.TrimSpace(productCode)
|
||||||
|
logger := utils.SlogFromContext(ctx).With(
|
||||||
|
"query", "production-product-costing.no-cost-detail-rows",
|
||||||
|
"recete_kodu", recipeCode,
|
||||||
|
"urun_kodu", productCode,
|
||||||
|
)
|
||||||
|
logger.Info("query dispatch")
|
||||||
|
|
||||||
|
sqlText := `
|
||||||
|
WITH RecipeMatch AS (
|
||||||
|
SELECT TOP 1
|
||||||
|
R.nUrtReceteID
|
||||||
|
FROM dbo.spUrtRecete R
|
||||||
|
WHERE LTRIM(RTRIM(ISNULL(R.sKodu, ''))) = @p1
|
||||||
|
AND (@p2 = '' OR LTRIM(RTRIM(ISNULL(R.sMModelKodu, ''))) = @p2)
|
||||||
|
ORDER BY
|
||||||
|
R.dteIslemTarihi DESC,
|
||||||
|
R.nUrtReceteID DESC
|
||||||
|
),
|
||||||
|
HammaddeTekil AS (
|
||||||
|
SELECT
|
||||||
|
ISNULL(NULLIF(LTRIM(RTRIM(HT.sAciklama3)), ''), ISNULL(NULLIF(LTRIM(RTRIM(HT.sAciklama)), ''), N'TANIMSIZ')) AS sAciklama3,
|
||||||
|
ISNULL(HT.nHammaddeTuruNo, 0) AS nHammaddeTuruNoSort,
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(HT.nHammaddeTuruNo, 0))) AS nHammaddeTuruNo,
|
||||||
|
-- Match URETIM's sp_pUrtOnMaliyetRecetedenKop behavior: use model code + color code instead of variant stock code.
|
||||||
|
ISNULL(S.sModel, ISNULL(S.sKodu, '')) AS sKodu,
|
||||||
|
ISNULL(S.sAciklama, '') AS sAciklama,
|
||||||
|
ISNULL(S.sRenk, '') AS sRenk,
|
||||||
|
ISNULL(HT.sAciklama, '') AS sHammaddeTuruAdi,
|
||||||
|
ISNULL(S.sBirimCinsi1, '') AS sBirim,
|
||||||
|
ISNULL(RMik.lHMiktar, 0) AS lMiktar,
|
||||||
|
ROW_NUMBER() OVER (
|
||||||
|
PARTITION BY HT.nHammaddeTuruNo
|
||||||
|
ORDER BY ISNULL(S.sModel, ISNULL(S.sKodu, ''))
|
||||||
|
) AS rn,
|
||||||
|
ROW_NUMBER() OVER (
|
||||||
|
ORDER BY HT.nHammaddeTuruNo, ISNULL(S.sModel, ISNULL(S.sKodu, ''))
|
||||||
|
) AS rowNo
|
||||||
|
FROM RecipeMatch R
|
||||||
|
INNER JOIN dbo.spUrtRecMBolumMik RMik
|
||||||
|
ON RMik.nUrtReceteID = R.nUrtReceteID
|
||||||
|
LEFT JOIN dbo.tbStok S
|
||||||
|
ON S.nStokID = RMik.nHStokID
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1
|
||||||
|
H.nHammaddeTuruNo,
|
||||||
|
H.sAciklama,
|
||||||
|
H.sAciklama3
|
||||||
|
FROM dbo.spUrtOnMLHammaddeTuru H
|
||||||
|
WHERE H.nUrtMBolumID = RMik.nUrtMBolumID
|
||||||
|
ORDER BY
|
||||||
|
CASE WHEN H.nUrtMTBolumID = RMik.nUrtMTBolumID THEN 0 ELSE 1 END,
|
||||||
|
H.nHammaddeTuruNo
|
||||||
|
) HT
|
||||||
|
WHERE HT.nHammaddeTuruNo IS NOT NULL
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
HT.sAciklama3,
|
||||||
|
0.0 AS GroupTotalTutar,
|
||||||
|
0.0 AS GroupTotalUSDTutar,
|
||||||
|
'' AS nOnMLNo,
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(HT.rowNo, 0))) AS nOnMLDetNo,
|
||||||
|
HT.nHammaddeTuruNo,
|
||||||
|
HT.sKodu,
|
||||||
|
HT.sAciklama,
|
||||||
|
HT.sRenk AS sRenk,
|
||||||
|
'' AS sBeden,
|
||||||
|
'' AS sAciklama2,
|
||||||
|
HT.lMiktar,
|
||||||
|
0.0 AS lFiyat,
|
||||||
|
0.0 AS lTutar,
|
||||||
|
'' AS sFiyatTipi,
|
||||||
|
'USD' AS sDovizCinsi,
|
||||||
|
0.0 AS lDovizKuru,
|
||||||
|
0.0 AS lDovizFiyati,
|
||||||
|
NULL AS fiyat_girilen,
|
||||||
|
'' AS fiyat_doviz,
|
||||||
|
CAST(1 AS bit) AS maliyete_dahil,
|
||||||
|
NULL AS cm_price_type_id,
|
||||||
|
0.0 AS usdTutar,
|
||||||
|
0.0 AS eurTutar,
|
||||||
|
0.0 AS gbpTutar,
|
||||||
|
HT.sBirim,
|
||||||
|
HT.sHammaddeTuruAdi,
|
||||||
|
HT.sHammaddeTuruAdi AS sParcaAdi
|
||||||
|
FROM HammaddeTekil HT
|
||||||
|
WHERE HT.rn = 1
|
||||||
|
ORDER BY
|
||||||
|
HT.nHammaddeTuruNoSort,
|
||||||
|
HT.sKodu
|
||||||
|
`
|
||||||
|
|
||||||
|
rows, err := uretimDB.QueryContext(ctx, sqlText, recipeCode, productCode)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("query error", "err", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return rows, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionTypes(ctx context.Context, uretimDB *sql.DB) (*sql.Rows, error) {
|
||||||
|
sqlText := `SELECT id, aciklama FROM dbo.mk_uretim_sekli ORDER BY id`
|
||||||
|
return uretimDB.QueryContext(ctx, sqlText)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionHasCostDetailHammaddeTypeOptions(
|
||||||
|
ctx context.Context,
|
||||||
|
uretimDB *sql.DB,
|
||||||
|
search string,
|
||||||
|
limit int,
|
||||||
|
) (*sql.Rows, error) {
|
||||||
|
search = strings.TrimSpace(search)
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 50
|
||||||
|
}
|
||||||
|
searchLike := "%" + search + "%"
|
||||||
|
|
||||||
|
sqlText := `
|
||||||
|
SELECT TOP (@p2)
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(T.nHammaddeTuruNo, 0))) AS nHammaddeTuruNo,
|
||||||
|
ISNULL(T.sAciklama, '') AS sHammaddeTuruAdi,
|
||||||
|
ISNULL(NULLIF(LTRIM(RTRIM(T.sAciklama3)), ''), N'TANIMSIZ') AS sAciklama3
|
||||||
|
FROM dbo.spUrtOnMLHammaddeTuru T
|
||||||
|
WHERE
|
||||||
|
ISNULL(T.bAktif, 0) = 1
|
||||||
|
AND (
|
||||||
|
@p1 = ''
|
||||||
|
OR RTRIM(CONVERT(VARCHAR(32), ISNULL(T.nHammaddeTuruNo, 0))) LIKE @p3
|
||||||
|
OR ISNULL(T.sAciklama, '') LIKE @p3
|
||||||
|
OR ISNULL(T.sAciklama3, '') LIKE @p3
|
||||||
|
)
|
||||||
|
ORDER BY
|
||||||
|
CASE
|
||||||
|
WHEN @p1 <> '' AND RTRIM(CONVERT(VARCHAR(32), ISNULL(T.nHammaddeTuruNo, 0))) = @p1 THEN 0
|
||||||
|
WHEN @p1 <> '' AND ISNULL(T.sAciklama, '') = @p1 THEN 1
|
||||||
|
ELSE 2
|
||||||
|
END,
|
||||||
|
T.nHammaddeTuruNo
|
||||||
|
`
|
||||||
|
|
||||||
|
return uretimDB.QueryContext(ctx, sqlText, search, limit, searchLike)
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildSQLServerFullTextPrefixQuery(search string) string {
|
||||||
|
terms := strings.Fields(strings.TrimSpace(search))
|
||||||
|
parts := make([]string, 0, len(terms))
|
||||||
|
|
||||||
|
for _, term := range terms {
|
||||||
|
cleaned := strings.Map(func(r rune) rune {
|
||||||
|
if unicode.IsLetter(r) || unicode.IsDigit(r) {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}, term)
|
||||||
|
if len([]rune(cleaned)) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts = append(parts, fmt.Sprintf("\"%s*\"", cleaned))
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(parts, " AND ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasProductionHasCostDetailItemFullTextIndex(
|
||||||
|
ctx context.Context,
|
||||||
|
uretimDB *sql.DB,
|
||||||
|
) bool {
|
||||||
|
var exists int
|
||||||
|
err := uretimDB.QueryRowContext(ctx, `
|
||||||
|
SELECT CASE
|
||||||
|
WHEN EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys.fulltext_indexes fi
|
||||||
|
INNER JOIN sys.fulltext_index_columns fic
|
||||||
|
ON fic.object_id = fi.object_id
|
||||||
|
INNER JOIN sys.columns c
|
||||||
|
ON c.object_id = fic.object_id
|
||||||
|
AND c.column_id = fic.column_id
|
||||||
|
WHERE fi.object_id = OBJECT_ID('dbo.tbStok')
|
||||||
|
AND c.name = 'sAciklama'
|
||||||
|
) THEN 1 ELSE 0
|
||||||
|
END`).Scan(&exists)
|
||||||
|
return err == nil && exists == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionHasCostDetailItemOptions(
|
||||||
|
ctx context.Context,
|
||||||
|
uretimDB *sql.DB,
|
||||||
|
search string,
|
||||||
|
limit int,
|
||||||
|
) (*sql.Rows, error) {
|
||||||
|
search = strings.TrimSpace(search)
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 50
|
||||||
|
}
|
||||||
|
if search == "" {
|
||||||
|
return uretimDB.QueryContext(ctx, `
|
||||||
|
SELECT TOP (0)
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(S.nStokID, 0))) AS nStokID,
|
||||||
|
LTRIM(RTRIM(CONVERT(NVARCHAR(255), ISNULL(S.sKodu, '')))) AS sKodu,
|
||||||
|
LTRIM(RTRIM(CONVERT(NVARCHAR(255), ISNULL(S.sAciklama, '')))) AS sAciklama,
|
||||||
|
LTRIM(RTRIM(CONVERT(NVARCHAR(255), ISNULL(S.sModel, '')))) AS sModel,
|
||||||
|
LTRIM(RTRIM(CONVERT(NVARCHAR(64), ISNULL(S.sBirimCinsi1, '')))) AS sBirim
|
||||||
|
FROM dbo.tbStok S`)
|
||||||
|
}
|
||||||
|
|
||||||
|
searchExact := search
|
||||||
|
searchPrefix := search + "%"
|
||||||
|
searchLike := "%" + search + "%"
|
||||||
|
searchLen := len([]rune(search))
|
||||||
|
numericStokID, numericErr := strconv.Atoi(search)
|
||||||
|
hasNumericStokID := numericErr == nil
|
||||||
|
fullTextSearch := buildSQLServerFullTextPrefixQuery(search)
|
||||||
|
useFullText := searchLen >= 3 && fullTextSearch != "" && hasProductionHasCostDetailItemFullTextIndex(ctx, uretimDB)
|
||||||
|
|
||||||
|
baseSelect := `
|
||||||
|
SELECT TOP (@p2)
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(S.nStokID, 0))) AS nStokID,
|
||||||
|
LTRIM(RTRIM(CONVERT(NVARCHAR(255), ISNULL(S.sKodu, '')))) AS sKodu,
|
||||||
|
LTRIM(RTRIM(CONVERT(NVARCHAR(255), ISNULL(S.sAciklama, '')))) AS sAciklama,
|
||||||
|
LTRIM(RTRIM(CONVERT(NVARCHAR(255), ISNULL(S.sModel, '')))) AS sModel,
|
||||||
|
LTRIM(RTRIM(CONVERT(NVARCHAR(64), ISNULL(S.sBirimCinsi1, '')))) AS sBirim
|
||||||
|
FROM dbo.tbStok S
|
||||||
|
WHERE
|
||||||
|
(ISNULL(S.IsBlocked, 0) = 0)
|
||||||
|
AND S.sModel LIKE '_.%%'
|
||||||
|
AND (
|
||||||
|
(@p5 = 1 AND S.nStokID = @p6)
|
||||||
|
OR S.sKodu = @p1
|
||||||
|
OR S.sKodu LIKE @p3
|
||||||
|
OR %s
|
||||||
|
)
|
||||||
|
ORDER BY
|
||||||
|
CASE
|
||||||
|
WHEN S.sKodu = @p1 THEN 0
|
||||||
|
WHEN (@p5 = 1 AND S.nStokID = @p6) THEN 1
|
||||||
|
WHEN S.sKodu LIKE @p3 THEN 2
|
||||||
|
ELSE 3
|
||||||
|
END,
|
||||||
|
S.sKodu
|
||||||
|
OPTION (RECOMPILE)
|
||||||
|
`
|
||||||
|
|
||||||
|
if useFullText {
|
||||||
|
sqlText := fmt.Sprintf(baseSelect, `CONTAINS(S.sAciklama, @p4)`)
|
||||||
|
return uretimDB.QueryContext(ctx, sqlText, searchExact, limit, searchPrefix, fullTextSearch, hasNumericStokID, numericStokID)
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlText := fmt.Sprintf(baseSelect, `(@p4 >= 3 AND S.sAciklama LIKE @p7)`)
|
||||||
|
return uretimDB.QueryContext(ctx, sqlText, searchExact, limit, searchPrefix, searchLen, hasNumericStokID, numericStokID, searchLike)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionHasCostDetailColorOptions(
|
||||||
|
ctx context.Context,
|
||||||
|
mssqlDB *sql.DB,
|
||||||
|
modelCode string,
|
||||||
|
search string,
|
||||||
|
limit int,
|
||||||
|
) (*sql.Rows, error) {
|
||||||
|
modelCode = strings.TrimSpace(modelCode)
|
||||||
|
search = strings.TrimSpace(search)
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 50
|
||||||
|
}
|
||||||
|
searchLike := "%" + search + "%"
|
||||||
|
|
||||||
|
sqlText := `
|
||||||
|
WITH ColorSource AS (
|
||||||
|
SELECT DISTINCT
|
||||||
|
LTRIM(RTRIM(CONVERT(NVARCHAR(64), ISNULL(T.sRenk, '')))) AS ColorCode
|
||||||
|
FROM dbo.tbStok T
|
||||||
|
WHERE ISNULL(T.IsBlocked, 0) = 0
|
||||||
|
AND LTRIM(RTRIM(CONVERT(NVARCHAR(255), ISNULL(T.sModel, '')))) = @p1
|
||||||
|
AND LTRIM(RTRIM(CONVERT(NVARCHAR(64), ISNULL(T.sRenk, '')))) <> ''
|
||||||
|
)
|
||||||
|
SELECT TOP (@p2)
|
||||||
|
LTRIM(RTRIM(ISNULL(S.ColorCode, ''))) AS colorCode,
|
||||||
|
ISNULL(C.ColorDescription, '') AS colorDescription
|
||||||
|
FROM (
|
||||||
|
SELECT ColorCode
|
||||||
|
FROM ColorSource
|
||||||
|
) S
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1 ColorDescription
|
||||||
|
FROM dbo.cdColorDesc CD
|
||||||
|
WHERE CD.LangCode = 'TR'
|
||||||
|
AND LTRIM(RTRIM(ISNULL(CD.ColorCode, ''))) = S.ColorCode
|
||||||
|
) C
|
||||||
|
WHERE @p1 <> ''
|
||||||
|
AND (
|
||||||
|
@p3 = ''
|
||||||
|
OR LTRIM(RTRIM(ISNULL(S.ColorCode, ''))) LIKE @p4
|
||||||
|
OR ISNULL(C.ColorDescription, '') LIKE @p4
|
||||||
|
)
|
||||||
|
ORDER BY
|
||||||
|
CASE
|
||||||
|
WHEN @p3 <> '' AND LTRIM(RTRIM(ISNULL(S.ColorCode, ''))) = @p3 THEN 0
|
||||||
|
WHEN @p3 <> '' AND ISNULL(C.ColorDescription, '') = @p3 THEN 1
|
||||||
|
ELSE 2
|
||||||
|
END,
|
||||||
|
LTRIM(RTRIM(ISNULL(S.ColorCode, '')))
|
||||||
|
`
|
||||||
|
|
||||||
|
return mssqlDB.QueryContext(ctx, sqlText, modelCode, limit, search, searchLike)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionHasCostDetailExchangeRatesByDate(
|
||||||
|
ctx context.Context,
|
||||||
|
mssqlDB *sql.DB,
|
||||||
|
costDate string,
|
||||||
|
) (*sql.Row, error) {
|
||||||
|
costDate = strings.TrimSpace(costDate)
|
||||||
|
|
||||||
|
sqlText := `
|
||||||
|
DECLARE @targetDate date = ISNULL(CONVERT(date, NULLIF(@p1, ''), 23), CONVERT(date, GETDATE()))
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
CONVERT(VARCHAR(10), @targetDate, 23) AS rateDate,
|
||||||
|
ISNULL((
|
||||||
|
SELECT TOP 1 Rate
|
||||||
|
FROM dbo.AllExchangeRates
|
||||||
|
WHERE CurrencyCode = 'USD'
|
||||||
|
AND RelationCurrencyCode = 'TRY'
|
||||||
|
AND ExchangeTypeCode = 6
|
||||||
|
AND Rate > 0
|
||||||
|
AND CONVERT(date, [Date]) <= @targetDate
|
||||||
|
ORDER BY
|
||||||
|
CASE WHEN CONVERT(date, [Date]) = @targetDate THEN 0 ELSE 1 END,
|
||||||
|
[Date] DESC
|
||||||
|
), 0) AS usdRate,
|
||||||
|
ISNULL((
|
||||||
|
SELECT TOP 1 Rate
|
||||||
|
FROM dbo.AllExchangeRates
|
||||||
|
WHERE CurrencyCode = 'EUR'
|
||||||
|
AND RelationCurrencyCode = 'TRY'
|
||||||
|
AND ExchangeTypeCode = 6
|
||||||
|
AND Rate > 0
|
||||||
|
AND CONVERT(date, [Date]) <= @targetDate
|
||||||
|
ORDER BY
|
||||||
|
CASE WHEN CONVERT(date, [Date]) = @targetDate THEN 0 ELSE 1 END,
|
||||||
|
[Date] DESC
|
||||||
|
), 0) AS eurRate,
|
||||||
|
ISNULL((
|
||||||
|
SELECT TOP 1 Rate
|
||||||
|
FROM dbo.AllExchangeRates
|
||||||
|
WHERE CurrencyCode = 'GBP'
|
||||||
|
AND RelationCurrencyCode = 'TRY'
|
||||||
|
AND ExchangeTypeCode = 6
|
||||||
|
AND Rate > 0
|
||||||
|
AND CONVERT(date, [Date]) <= @targetDate
|
||||||
|
ORDER BY
|
||||||
|
CASE WHEN CONVERT(date, [Date]) = @targetDate THEN 0 ELSE 1 END,
|
||||||
|
[Date] DESC
|
||||||
|
), 0) AS gbpRate
|
||||||
|
`
|
||||||
|
|
||||||
|
return mssqlDB.QueryRowContext(ctx, sqlText, costDate), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionHasCostLatestPurchasePriceForItem(
|
||||||
|
ctx context.Context,
|
||||||
|
mssqlDB *sql.DB,
|
||||||
|
sKodu string,
|
||||||
|
colorCode string,
|
||||||
|
itemDim1Code string,
|
||||||
|
costDate string,
|
||||||
|
) (*sql.Row, error) {
|
||||||
|
sKodu = strings.TrimSpace(sKodu)
|
||||||
|
colorCode = strings.TrimSpace(colorCode)
|
||||||
|
itemDim1Code = strings.TrimSpace(itemDim1Code)
|
||||||
|
costDate = strings.TrimSpace(costDate)
|
||||||
|
|
||||||
|
sqlText := `
|
||||||
|
WITH BASE AS (
|
||||||
|
SELECT
|
||||||
|
A.InvoiceDate,
|
||||||
|
A.InvoiceNumber,
|
||||||
|
A.ProcessCode,
|
||||||
|
A.CurrAccTypeCode,
|
||||||
|
A.CurrAccCode,
|
||||||
|
A.ItemTypeCode,
|
||||||
|
A.ItemCode,
|
||||||
|
A.ColorCode,
|
||||||
|
A.ItemDim1Code,
|
||||||
|
A.Qty1,
|
||||||
|
A.Doc_Price,
|
||||||
|
A.Doc_Amount,
|
||||||
|
A.Doc_CurrencyCode
|
||||||
|
FROM AllInvoicesWithAttributes A
|
||||||
|
WHERE A.ProcessCode IN ('BP')
|
||||||
|
AND A.ATAtt01 IN (1, 2)
|
||||||
|
AND A.CompanyCode IN (1, 2, 5)
|
||||||
|
AND A.IsCompleted = 1
|
||||||
|
AND YEAR(A.InvoiceDate) >= 2022
|
||||||
|
AND LTRIM(RTRIM(A.ItemCode)) = @p1
|
||||||
|
AND (NULLIF(@p2, '') IS NULL OR CONVERT(date, A.InvoiceDate) < CONVERT(date, NULLIF(@p2, ''), 23))
|
||||||
|
)
|
||||||
|
SELECT TOP 1
|
||||||
|
'MAN' AS priceType,
|
||||||
|
CONVERT(VARCHAR(16), B.InvoiceDate, 120) AS Tarih,
|
||||||
|
ISNULL(B.InvoiceNumber, '') AS FaturaKodu,
|
||||||
|
LTRIM(RTRIM(ISNULL(B.ItemCode, ''))) AS MasrafKodu,
|
||||||
|
ISNULL(ID.ItemDescription, '') AS MasrafDetay,
|
||||||
|
ISNULL(B.ColorCode, '') AS ColorCode,
|
||||||
|
ISNULL(COL.ColorDescription, '') AS ColorDescription,
|
||||||
|
ISNULL(B.ItemDim1Code, '') AS ItemDim1Code,
|
||||||
|
ISNULL(DIM1.ItemDim1Description, '') AS ItemDim1Description,
|
||||||
|
ISNULL(B.Doc_Price, 0) AS EvrakFiyat,
|
||||||
|
ISNULL(B.Doc_CurrencyCode, '') AS EvrakDoviz
|
||||||
|
FROM BASE B
|
||||||
|
LEFT JOIN cdItem CI
|
||||||
|
ON CI.ItemTypeCode = B.ItemTypeCode
|
||||||
|
AND CI.ItemCode = B.ItemCode
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1 ItemDescription
|
||||||
|
FROM cdItemDesc
|
||||||
|
WHERE ItemTypeCode = B.ItemTypeCode
|
||||||
|
AND ItemCode = B.ItemCode
|
||||||
|
AND LangCode = 'TR'
|
||||||
|
) ID
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1 ItemDim1Description
|
||||||
|
FROM cdItemDim1Desc
|
||||||
|
WHERE ItemDim1Code = B.ItemDim1Code
|
||||||
|
AND LangCode = 'TR'
|
||||||
|
) DIM1
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1 ColorDescription
|
||||||
|
FROM cdColorDesc
|
||||||
|
WHERE ColorCode = B.ColorCode
|
||||||
|
AND LangCode = 'TR'
|
||||||
|
) COL
|
||||||
|
ORDER BY
|
||||||
|
CASE
|
||||||
|
WHEN @p3 <> '' AND LTRIM(RTRIM(ISNULL(B.ColorCode, ''))) = @p3 THEN 0
|
||||||
|
WHEN @p3 = '' THEN 0
|
||||||
|
ELSE 1
|
||||||
|
END,
|
||||||
|
CASE
|
||||||
|
WHEN @p4 <> '' AND LTRIM(RTRIM(ISNULL(B.ItemDim1Code, ''))) = @p4 THEN 0
|
||||||
|
WHEN @p4 = '' THEN 0
|
||||||
|
ELSE 1
|
||||||
|
END,
|
||||||
|
B.InvoiceDate DESC,
|
||||||
|
B.InvoiceNumber DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
return mssqlDB.QueryRowContext(ctx, sqlText, sKodu, costDate, colorCode, itemDim1Code), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionHasCostPurchaseHistoryByExpenseCode(
|
||||||
|
ctx context.Context,
|
||||||
|
mssqlDB *sql.DB,
|
||||||
|
sKodu string,
|
||||||
|
costDate string,
|
||||||
|
limit int,
|
||||||
|
) (*sql.Rows, error) {
|
||||||
|
sKodu = strings.TrimSpace(sKodu)
|
||||||
|
costDate = strings.TrimSpace(costDate)
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 250
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlText := `
|
||||||
|
WITH BASE AS (
|
||||||
|
SELECT
|
||||||
|
A.InvoiceDate,
|
||||||
|
A.InvoiceNumber,
|
||||||
|
A.ProcessCode,
|
||||||
|
A.CurrAccTypeCode,
|
||||||
|
A.CurrAccCode,
|
||||||
|
A.ItemTypeCode,
|
||||||
|
A.ItemCode,
|
||||||
|
A.ColorCode,
|
||||||
|
A.ItemDim1Code,
|
||||||
|
A.Qty1,
|
||||||
|
A.Doc_Price,
|
||||||
|
A.Doc_Amount,
|
||||||
|
A.Doc_CurrencyCode
|
||||||
|
FROM AllInvoicesWithAttributes A
|
||||||
|
WHERE A.ProcessCode IN ('BP')
|
||||||
|
AND A.ATAtt01 IN (1, 2)
|
||||||
|
AND A.CompanyCode IN (1, 2, 5)
|
||||||
|
AND A.IsCompleted = 1
|
||||||
|
AND YEAR(A.InvoiceDate) >= 2022
|
||||||
|
AND LTRIM(RTRIM(A.ItemCode)) = @p1
|
||||||
|
AND (ISNULL(A.Doc_Price, 0) > 0 OR ISNULL(A.Doc_Amount, 0) > 0)
|
||||||
|
AND (NULLIF(@p2, '') IS NULL OR CONVERT(date, A.InvoiceDate) < CONVERT(date, NULLIF(@p2, ''), 23))
|
||||||
|
)
|
||||||
|
SELECT TOP (@p3)
|
||||||
|
'purchase' AS sourceType,
|
||||||
|
'MAN' AS priceType,
|
||||||
|
CONVERT(VARCHAR(16), B.InvoiceDate, 120) AS Tarih,
|
||||||
|
ISNULL(B.InvoiceNumber, '') AS FaturaKodu,
|
||||||
|
ISNULL(B.CurrAccCode, '') AS FirmaKodu,
|
||||||
|
ISNULL(CAD.CurrAccDescription, '') AS FirmaAciklama,
|
||||||
|
LTRIM(RTRIM(ISNULL(B.ItemCode, ''))) AS MasrafKodu,
|
||||||
|
ISNULL(ID.ItemDescription, '') AS MasrafDetay,
|
||||||
|
ISNULL(B.ColorCode, '') AS ColorCode,
|
||||||
|
ISNULL(COL.ColorDescription, '') AS ColorDescription,
|
||||||
|
ISNULL(B.ItemDim1Code, '') AS ItemDim1Code,
|
||||||
|
ISNULL(DIM1.ItemDim1Description, '') AS ItemDim1Description,
|
||||||
|
ISNULL(B.Qty1, 0) AS Miktar,
|
||||||
|
CASE
|
||||||
|
WHEN B.ProcessCode = 'EP' THEN ''
|
||||||
|
ELSE ISNULL(CI.UnitOfMeasureCode1, '')
|
||||||
|
END AS BIRIM,
|
||||||
|
ISNULL(B.Doc_Price, 0) AS EvrakFiyat,
|
||||||
|
ISNULL(B.Doc_Amount, 0) AS EvrakTutar,
|
||||||
|
ISNULL(B.Doc_CurrencyCode, '') AS EvrakDoviz
|
||||||
|
FROM BASE B
|
||||||
|
LEFT JOIN cdItem CI
|
||||||
|
ON CI.ItemTypeCode = B.ItemTypeCode
|
||||||
|
AND CI.ItemCode = B.ItemCode
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1 ItemDescription
|
||||||
|
FROM cdItemDesc
|
||||||
|
WHERE ItemTypeCode = B.ItemTypeCode
|
||||||
|
AND ItemCode = B.ItemCode
|
||||||
|
AND LangCode = 'TR'
|
||||||
|
) ID
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1 ItemDim1Description
|
||||||
|
FROM cdItemDim1Desc
|
||||||
|
WHERE ItemDim1Code = B.ItemDim1Code
|
||||||
|
AND LangCode = 'TR'
|
||||||
|
) DIM1
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1 ColorDescription
|
||||||
|
FROM cdColorDesc
|
||||||
|
WHERE ColorCode = B.ColorCode
|
||||||
|
AND LangCode = 'TR'
|
||||||
|
) COL
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1 CurrAccDescription
|
||||||
|
FROM cdCurrAccDesc
|
||||||
|
WHERE CurrAccTypeCode = B.CurrAccTypeCode
|
||||||
|
AND CurrAccCode = B.CurrAccCode
|
||||||
|
AND LangCode = 'TR'
|
||||||
|
) CAD
|
||||||
|
ORDER BY
|
||||||
|
B.InvoiceDate DESC,
|
||||||
|
B.InvoiceNumber DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
return mssqlDB.QueryContext(ctx, sqlText, sKodu, costDate, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionHasCostRecipeHistoryByExpenseCode(
|
||||||
|
ctx context.Context,
|
||||||
|
uretimDB *sql.DB,
|
||||||
|
currentOnMLNo int,
|
||||||
|
sKodu string,
|
||||||
|
colorCode string,
|
||||||
|
costDate string,
|
||||||
|
limit int,
|
||||||
|
) (*sql.Rows, error) {
|
||||||
|
sKodu = strings.TrimSpace(sKodu)
|
||||||
|
colorCode = strings.TrimSpace(colorCode)
|
||||||
|
costDate = strings.TrimSpace(costDate)
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 250
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlText := `
|
||||||
|
SELECT TOP (@p5)
|
||||||
|
'recipe' AS sourceType,
|
||||||
|
ISNULL(NULLIF(LTRIM(RTRIM(D.sFiyatTipi)), ''), 'SAF') AS priceType,
|
||||||
|
CONVERT(VARCHAR(16), COALESCE(D.dteIslemTarihiDeg, D.dteIslemTarihi, M.Tarihi, M.dteKayitTarihi), 120) AS dteIslemTarihi,
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(D.nOnMLNo, 0))) AS nOnMLNo,
|
||||||
|
ISNULL(UF.FirmaKodu, '') AS FirmaKodu,
|
||||||
|
ISNULL(UF.FirmaAciklama, '') AS FirmaAciklama,
|
||||||
|
ISNULL(D.sKodu, '') AS sKodu,
|
||||||
|
ISNULL(D.sAciklama, '') AS sAciklama,
|
||||||
|
ISNULL(D.sRenk, '') AS sRenk,
|
||||||
|
ISNULL(D.lMiktar, 0) AS lMiktar,
|
||||||
|
ISNULL(D.sBirim, '') AS sBirim,
|
||||||
|
CASE
|
||||||
|
WHEN ISNULL(D.fiyat_girilen, 0) > 0 THEN ISNULL(D.fiyat_girilen, 0)
|
||||||
|
ELSE ISNULL(D.lDovizFiyati, 0)
|
||||||
|
END AS lDovizFiyati,
|
||||||
|
CASE
|
||||||
|
WHEN ISNULL(D.lDovizTutari, 0) > 0 THEN ISNULL(D.lDovizTutari, 0)
|
||||||
|
WHEN ISNULL(D.fiyat_girilen, 0) > 0 THEN ISNULL(D.fiyat_girilen, 0) * ISNULL(D.lMiktar, 0)
|
||||||
|
ELSE ISNULL(D.lDovizFiyati, 0) * ISNULL(D.lMiktar, 0)
|
||||||
|
END AS lDovizTutari,
|
||||||
|
CASE
|
||||||
|
WHEN LTRIM(RTRIM(ISNULL(D.fiyat_doviz, ''))) <> '' THEN LTRIM(RTRIM(D.fiyat_doviz))
|
||||||
|
WHEN LTRIM(RTRIM(ISNULL(D.sDovizCinsi, ''))) <> '' THEN LTRIM(RTRIM(D.sDovizCinsi))
|
||||||
|
WHEN LTRIM(RTRIM(ISNULL(M.sDovizCinsi, ''))) <> '' THEN LTRIM(RTRIM(M.sDovizCinsi))
|
||||||
|
ELSE 'USD'
|
||||||
|
END AS USD,
|
||||||
|
ISNULL(NULLIF(LTRIM(RTRIM(T.sAciklama3)), ''), N'DUMMY') AS DUMMY
|
||||||
|
FROM dbo.spUrtOnMLMasDet D
|
||||||
|
INNER JOIN dbo.spUrtOnMLMas M
|
||||||
|
ON M.nOnMLNo = D.nOnMLNo
|
||||||
|
LEFT JOIN dbo.spUrtOnMLHammaddeTuru T
|
||||||
|
ON T.nHammaddeTuruNo = D.nHammaddeTuruNo
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1
|
||||||
|
ISNULL(F.sKodu, '') AS FirmaKodu,
|
||||||
|
ISNULL(F.sAciklama, '') AS FirmaAciklama
|
||||||
|
FROM dbo.spUrtSiparisDet SD
|
||||||
|
INNER JOIN dbo.spUrtSiparis SM
|
||||||
|
ON SM.nUrtSiparisID = SD.nUrtSiparisID
|
||||||
|
LEFT JOIN dbo.tbFirma F
|
||||||
|
ON F.nFirmaID = SM.nFirmaID
|
||||||
|
WHERE (
|
||||||
|
(ISNULL(M.nUrtReceteID, 0) > 0 AND SD.nUrtReceteID = M.nUrtReceteID)
|
||||||
|
OR (ISNULL(M.nUrtReceteID, 0) <= 0 AND LTRIM(RTRIM(ISNULL(SD.sMModelKodu, ''))) = LTRIM(RTRIM(ISNULL(M.UrunKodu, ''))))
|
||||||
|
)
|
||||||
|
AND SD.dteIslemTarihi <= COALESCE(D.dteIslemTarihiDeg, D.dteIslemTarihi, M.Tarihi, M.dteKayitTarihi)
|
||||||
|
ORDER BY SD.dteIslemTarihi DESC, SD.nUrtSiparisID DESC
|
||||||
|
) UF
|
||||||
|
WHERE LTRIM(RTRIM(D.sKodu)) = @p1
|
||||||
|
AND (@p2 <= 0 OR D.nOnMLNo <> @p2)
|
||||||
|
AND (
|
||||||
|
ISNULL(D.fiyat_girilen, 0) > 0
|
||||||
|
OR ISNULL(D.lDovizFiyati, 0) > 0
|
||||||
|
OR ISNULL(D.lDovizTutari, 0) > 0
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
NULLIF(@p3, '') IS NULL
|
||||||
|
OR CONVERT(date, COALESCE(D.dteIslemTarihiDeg, D.dteIslemTarihi, M.Tarihi, M.dteKayitTarihi)) < CONVERT(date, NULLIF(@p3, ''), 23)
|
||||||
|
)
|
||||||
|
ORDER BY
|
||||||
|
CASE
|
||||||
|
WHEN @p4 <> '' AND LTRIM(RTRIM(ISNULL(D.sRenk, ''))) = @p4 THEN 0
|
||||||
|
WHEN @p4 = '' THEN 0
|
||||||
|
ELSE 1
|
||||||
|
END,
|
||||||
|
COALESCE(D.dteIslemTarihiDeg, D.dteIslemTarihi, M.Tarihi, M.dteKayitTarihi) DESC,
|
||||||
|
D.nOnMLNo DESC,
|
||||||
|
D.nOnMLDetNo DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
return uretimDB.QueryContext(ctx, sqlText, sKodu, currentOnMLNo, costDate, colorCode, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BuildProductionHasCostSimilarCodePrefix(sKodu string) string {
|
||||||
|
normalizedCode := strings.ToUpper(strings.TrimSpace(sKodu))
|
||||||
|
if normalizedCode == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
runes := []rune(normalizedCode)
|
||||||
|
if len(runes) <= 4 {
|
||||||
|
return normalizedCode
|
||||||
|
}
|
||||||
|
if len(runes) >= 5 && len(runes) > 1 && runes[1] == '.' {
|
||||||
|
return string(runes[:5])
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(runes[:4])
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionHasCostPurchaseHistoryByCodePrefix(
|
||||||
|
ctx context.Context,
|
||||||
|
mssqlDB *sql.DB,
|
||||||
|
sKoduPrefix string,
|
||||||
|
costDate string,
|
||||||
|
limit int,
|
||||||
|
) (*sql.Rows, error) {
|
||||||
|
sKoduPrefix = strings.TrimSpace(sKoduPrefix)
|
||||||
|
costDate = strings.TrimSpace(costDate)
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 250
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlText := `
|
||||||
|
WITH BASE AS (
|
||||||
|
SELECT
|
||||||
|
A.InvoiceDate,
|
||||||
|
A.InvoiceNumber,
|
||||||
|
A.ProcessCode,
|
||||||
|
A.CurrAccTypeCode,
|
||||||
|
A.CurrAccCode,
|
||||||
|
A.ItemTypeCode,
|
||||||
|
A.ItemCode,
|
||||||
|
A.ColorCode,
|
||||||
|
A.ItemDim1Code,
|
||||||
|
A.Qty1,
|
||||||
|
A.Doc_Price,
|
||||||
|
A.Doc_Amount,
|
||||||
|
A.Doc_CurrencyCode
|
||||||
|
FROM AllInvoicesWithAttributes A
|
||||||
|
WHERE A.ProcessCode IN ('BP')
|
||||||
|
AND A.ATAtt01 IN (1, 2)
|
||||||
|
AND A.CompanyCode IN (1, 2, 5)
|
||||||
|
AND A.IsCompleted = 1
|
||||||
|
AND YEAR(A.InvoiceDate) >= 2022
|
||||||
|
AND NULLIF(@p1, '') IS NOT NULL
|
||||||
|
AND LTRIM(RTRIM(A.ItemCode)) LIKE @p1 + '%'
|
||||||
|
AND (ISNULL(A.Doc_Price, 0) > 0 OR ISNULL(A.Doc_Amount, 0) > 0)
|
||||||
|
AND (NULLIF(@p2, '') IS NULL OR CONVERT(date, A.InvoiceDate) < CONVERT(date, NULLIF(@p2, ''), 23))
|
||||||
|
)
|
||||||
|
SELECT TOP (@p3)
|
||||||
|
'purchase' AS sourceType,
|
||||||
|
'BNZ' AS priceType,
|
||||||
|
CONVERT(VARCHAR(16), B.InvoiceDate, 120) AS Tarih,
|
||||||
|
ISNULL(B.InvoiceNumber, '') AS FaturaKodu,
|
||||||
|
ISNULL(B.CurrAccCode, '') AS FirmaKodu,
|
||||||
|
ISNULL(CAD.CurrAccDescription, '') AS FirmaAciklama,
|
||||||
|
LTRIM(RTRIM(ISNULL(B.ItemCode, ''))) AS MasrafKodu,
|
||||||
|
ISNULL(ID.ItemDescription, '') AS MasrafDetay,
|
||||||
|
ISNULL(B.ColorCode, '') AS ColorCode,
|
||||||
|
ISNULL(COL.ColorDescription, '') AS ColorDescription,
|
||||||
|
ISNULL(B.ItemDim1Code, '') AS ItemDim1Code,
|
||||||
|
ISNULL(DIM1.ItemDim1Description, '') AS ItemDim1Description,
|
||||||
|
ISNULL(B.Qty1, 0) AS Miktar,
|
||||||
|
CASE
|
||||||
|
WHEN B.ProcessCode = 'EP' THEN ''
|
||||||
|
ELSE ISNULL(CI.UnitOfMeasureCode1, '')
|
||||||
|
END AS BIRIM,
|
||||||
|
ISNULL(B.Doc_Price, 0) AS EvrakFiyat,
|
||||||
|
ISNULL(B.Doc_Amount, 0) AS EvrakTutar,
|
||||||
|
ISNULL(B.Doc_CurrencyCode, '') AS EvrakDoviz
|
||||||
|
FROM BASE B
|
||||||
|
LEFT JOIN cdItem CI
|
||||||
|
ON CI.ItemTypeCode = B.ItemTypeCode
|
||||||
|
AND CI.ItemCode = B.ItemCode
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1 ItemDescription
|
||||||
|
FROM cdItemDesc
|
||||||
|
WHERE ItemTypeCode = B.ItemTypeCode
|
||||||
|
AND ItemCode = B.ItemCode
|
||||||
|
AND LangCode = 'TR'
|
||||||
|
) ID
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1 ItemDim1Description
|
||||||
|
FROM cdItemDim1Desc
|
||||||
|
WHERE ItemDim1Code = B.ItemDim1Code
|
||||||
|
AND LangCode = 'TR'
|
||||||
|
) DIM1
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1 ColorDescription
|
||||||
|
FROM cdColorDesc
|
||||||
|
WHERE ColorCode = B.ColorCode
|
||||||
|
AND LangCode = 'TR'
|
||||||
|
) COL
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1 CurrAccDescription
|
||||||
|
FROM cdCurrAccDesc
|
||||||
|
WHERE CurrAccTypeCode = B.CurrAccTypeCode
|
||||||
|
AND CurrAccCode = B.CurrAccCode
|
||||||
|
AND LangCode = 'TR'
|
||||||
|
) CAD
|
||||||
|
ORDER BY
|
||||||
|
B.InvoiceDate DESC,
|
||||||
|
B.InvoiceNumber DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
return mssqlDB.QueryContext(ctx, sqlText, sKoduPrefix, costDate, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionHasCostOnMLHistoryByCodePrefix(
|
||||||
|
ctx context.Context,
|
||||||
|
uretimDB *sql.DB,
|
||||||
|
sKoduPrefix string,
|
||||||
|
costDate string,
|
||||||
|
limit int,
|
||||||
|
) (*sql.Rows, error) {
|
||||||
|
sKoduPrefix = strings.TrimSpace(sKoduPrefix)
|
||||||
|
costDate = strings.TrimSpace(costDate)
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 250
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlText := `
|
||||||
|
SELECT TOP (@p3)
|
||||||
|
'recipe' AS sourceType,
|
||||||
|
'BNZ' AS priceType,
|
||||||
|
CONVERT(VARCHAR(16), COALESCE(D.dteIslemTarihiDeg, D.dteIslemTarihi, M.Tarihi, M.dteKayitTarihi), 120) AS dteIslemTarihi,
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(D.nOnMLNo, 0))) AS nOnMLNo,
|
||||||
|
ISNULL(UF.FirmaKodu, '') AS FirmaKodu,
|
||||||
|
ISNULL(UF.FirmaAciklama, '') AS FirmaAciklama,
|
||||||
|
ISNULL(D.sKodu, '') AS sKodu,
|
||||||
|
ISNULL(D.sAciklama, '') AS sAciklama,
|
||||||
|
ISNULL(D.sRenk, '') AS sRenk,
|
||||||
|
ISNULL(D.lMiktar, 0) AS lMiktar,
|
||||||
|
ISNULL(D.sBirim, '') AS sBirim,
|
||||||
|
CASE
|
||||||
|
WHEN ISNULL(D.fiyat_girilen, 0) > 0 THEN ISNULL(D.fiyat_girilen, 0)
|
||||||
|
ELSE ISNULL(D.lDovizFiyati, 0)
|
||||||
|
END AS lDovizFiyati,
|
||||||
|
CASE
|
||||||
|
WHEN ISNULL(D.lDovizTutari, 0) > 0 THEN ISNULL(D.lDovizTutari, 0)
|
||||||
|
WHEN ISNULL(D.fiyat_girilen, 0) > 0 THEN ISNULL(D.fiyat_girilen, 0) * ISNULL(D.lMiktar, 0)
|
||||||
|
ELSE ISNULL(D.lDovizFiyati, 0) * ISNULL(D.lMiktar, 0)
|
||||||
|
END AS lDovizTutari,
|
||||||
|
CASE
|
||||||
|
WHEN LTRIM(RTRIM(ISNULL(D.fiyat_doviz, ''))) <> '' THEN LTRIM(RTRIM(D.fiyat_doviz))
|
||||||
|
WHEN LTRIM(RTRIM(ISNULL(D.sDovizCinsi, ''))) <> '' THEN LTRIM(RTRIM(D.sDovizCinsi))
|
||||||
|
WHEN LTRIM(RTRIM(ISNULL(M.sDovizCinsi, ''))) <> '' THEN LTRIM(RTRIM(M.sDovizCinsi))
|
||||||
|
ELSE 'USD'
|
||||||
|
END AS USD,
|
||||||
|
ISNULL(NULLIF(LTRIM(RTRIM(D.sAciklama3)), ''), N'DUMMY') AS DUMMY
|
||||||
|
FROM dbo.spUrtOnMLMasDet D
|
||||||
|
INNER JOIN dbo.spUrtOnMLMas M
|
||||||
|
ON M.nOnMLNo = D.nOnMLNo
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1
|
||||||
|
ISNULL(F.sKodu, '') AS FirmaKodu,
|
||||||
|
ISNULL(F.sAciklama, '') AS FirmaAciklama
|
||||||
|
FROM dbo.spUrtSiparisDet SD
|
||||||
|
INNER JOIN dbo.spUrtSiparis SM
|
||||||
|
ON SM.nUrtSiparisID = SD.nUrtSiparisID
|
||||||
|
LEFT JOIN dbo.tbFirma F
|
||||||
|
ON F.nFirmaID = SM.nFirmaID
|
||||||
|
WHERE (
|
||||||
|
(ISNULL(M.nUrtReceteID, 0) > 0 AND SD.nUrtReceteID = M.nUrtReceteID)
|
||||||
|
OR (ISNULL(M.nUrtReceteID, 0) <= 0 AND LTRIM(RTRIM(ISNULL(SD.sMModelKodu, ''))) = LTRIM(RTRIM(ISNULL(M.UrunKodu, ''))))
|
||||||
|
)
|
||||||
|
AND SD.dteIslemTarihi <= COALESCE(D.dteIslemTarihiDeg, D.dteIslemTarihi, M.Tarihi, M.dteKayitTarihi)
|
||||||
|
ORDER BY SD.dteIslemTarihi DESC, SD.nUrtSiparisID DESC
|
||||||
|
) UF
|
||||||
|
WHERE NULLIF(@p1, '') IS NOT NULL
|
||||||
|
AND LTRIM(RTRIM(ISNULL(D.sKodu, ''))) LIKE @p1 + '%'
|
||||||
|
AND (
|
||||||
|
ISNULL(D.fiyat_girilen, 0) > 0
|
||||||
|
OR ISNULL(D.lDovizFiyati, 0) > 0
|
||||||
|
OR ISNULL(D.lDovizTutari, 0) > 0
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
NULLIF(@p2, '') IS NULL
|
||||||
|
OR CONVERT(date, COALESCE(D.dteIslemTarihiDeg, D.dteIslemTarihi, M.Tarihi, M.dteKayitTarihi)) < CONVERT(date, NULLIF(@p2, ''), 23)
|
||||||
|
)
|
||||||
|
ORDER BY
|
||||||
|
COALESCE(D.dteIslemTarihiDeg, D.dteIslemTarihi, M.Tarihi, M.dteKayitTarihi) DESC,
|
||||||
|
D.nOnMLNo DESC,
|
||||||
|
D.nOnMLDetNo DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
return uretimDB.QueryContext(ctx, sqlText, sKoduPrefix, costDate, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionHasCostOnMLHistoryByHammaddeTuruNo(
|
||||||
|
ctx context.Context,
|
||||||
|
uretimDB *sql.DB,
|
||||||
|
nHammaddeTuruNo string,
|
||||||
|
costDate string,
|
||||||
|
limit int,
|
||||||
|
) (*sql.Rows, error) {
|
||||||
|
nHammaddeTuruNo = strings.TrimSpace(nHammaddeTuruNo)
|
||||||
|
costDate = strings.TrimSpace(costDate)
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 250
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlText := `
|
||||||
|
SELECT TOP (@p3)
|
||||||
|
'recipe' AS sourceType,
|
||||||
|
'BNZ' AS priceType,
|
||||||
|
CONVERT(VARCHAR(16), COALESCE(D.dteIslemTarihiDeg, D.dteIslemTarihi, M.Tarihi, M.dteKayitTarihi), 120) AS dteIslemTarihi,
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(D.nOnMLNo, 0))) AS nOnMLNo,
|
||||||
|
ISNULL(UF.FirmaKodu, '') AS FirmaKodu,
|
||||||
|
ISNULL(UF.FirmaAciklama, '') AS FirmaAciklama,
|
||||||
|
ISNULL(D.sKodu, '') AS sKodu,
|
||||||
|
ISNULL(D.sAciklama, '') AS sAciklama,
|
||||||
|
ISNULL(D.sRenk, '') AS sRenk,
|
||||||
|
ISNULL(D.lMiktar, 0) AS lMiktar,
|
||||||
|
ISNULL(D.sBirim, '') AS sBirim,
|
||||||
|
CASE
|
||||||
|
WHEN ISNULL(D.fiyat_girilen, 0) > 0 THEN ISNULL(D.fiyat_girilen, 0)
|
||||||
|
ELSE ISNULL(D.lDovizFiyati, 0)
|
||||||
|
END AS lDovizFiyati,
|
||||||
|
CASE
|
||||||
|
WHEN ISNULL(D.lDovizTutari, 0) > 0 THEN ISNULL(D.lDovizTutari, 0)
|
||||||
|
WHEN ISNULL(D.fiyat_girilen, 0) > 0 THEN ISNULL(D.fiyat_girilen, 0) * ISNULL(D.lMiktar, 0)
|
||||||
|
ELSE ISNULL(D.lDovizFiyati, 0) * ISNULL(D.lMiktar, 0)
|
||||||
|
END AS lDovizTutari,
|
||||||
|
CASE
|
||||||
|
WHEN LTRIM(RTRIM(ISNULL(D.fiyat_doviz, ''))) <> '' THEN LTRIM(RTRIM(D.fiyat_doviz))
|
||||||
|
WHEN LTRIM(RTRIM(ISNULL(D.sDovizCinsi, ''))) <> '' THEN LTRIM(RTRIM(D.sDovizCinsi))
|
||||||
|
WHEN LTRIM(RTRIM(ISNULL(M.sDovizCinsi, ''))) <> '' THEN LTRIM(RTRIM(M.sDovizCinsi))
|
||||||
|
ELSE 'USD'
|
||||||
|
END AS USD,
|
||||||
|
ISNULL(NULLIF(LTRIM(RTRIM(D.sAciklama3)), ''), N'DUMMY') AS DUMMY
|
||||||
|
FROM dbo.spUrtOnMLMasDet D
|
||||||
|
INNER JOIN dbo.spUrtOnMLMas M
|
||||||
|
ON M.nOnMLNo = D.nOnMLNo
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1
|
||||||
|
ISNULL(F.sKodu, '') AS FirmaKodu,
|
||||||
|
ISNULL(F.sAciklama, '') AS FirmaAciklama
|
||||||
|
FROM dbo.spUrtSiparisDet SD
|
||||||
|
INNER JOIN dbo.spUrtSiparis SM
|
||||||
|
ON SM.nUrtSiparisID = SD.nUrtSiparisID
|
||||||
|
LEFT JOIN dbo.tbFirma F
|
||||||
|
ON F.nFirmaID = SM.nFirmaID
|
||||||
|
WHERE (
|
||||||
|
(ISNULL(M.nUrtReceteID, 0) > 0 AND SD.nUrtReceteID = M.nUrtReceteID)
|
||||||
|
OR (ISNULL(M.nUrtReceteID, 0) <= 0 AND LTRIM(RTRIM(ISNULL(SD.sMModelKodu, ''))) = LTRIM(RTRIM(ISNULL(M.UrunKodu, ''))))
|
||||||
|
)
|
||||||
|
AND SD.dteIslemTarihi <= COALESCE(D.dteIslemTarihiDeg, D.dteIslemTarihi, M.Tarihi, M.dteKayitTarihi)
|
||||||
|
ORDER BY SD.dteIslemTarihi DESC, SD.nUrtSiparisID DESC
|
||||||
|
) UF
|
||||||
|
WHERE D.nHammaddeTuruNo = @p1
|
||||||
|
AND ISNULL(D.fiyat_girilen, 0) > 0
|
||||||
|
AND (
|
||||||
|
NULLIF(@p2, '') IS NULL
|
||||||
|
OR CONVERT(date, COALESCE(D.dteIslemTarihiDeg, D.dteIslemTarihi, M.Tarihi, M.dteKayitTarihi)) < CONVERT(date, NULLIF(@p2, ''), 23)
|
||||||
|
)
|
||||||
|
ORDER BY
|
||||||
|
COALESCE(D.dteIslemTarihiDeg, D.dteIslemTarihi, M.Tarihi, M.dteKayitTarihi) DESC,
|
||||||
|
D.nOnMLNo DESC,
|
||||||
|
D.nOnMLDetNo DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
return uretimDB.QueryContext(ctx, sqlText, nHammaddeTuruNo, costDate, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetProductionHasCostSimilarItemHistory(
|
||||||
|
ctx context.Context,
|
||||||
|
mssqlDB *sql.DB,
|
||||||
|
uretimDB *sql.DB,
|
||||||
|
nHammaddeTuruNo string,
|
||||||
|
costDate string,
|
||||||
|
limit int,
|
||||||
|
) ([]any, error) {
|
||||||
|
if limit <= 0 {
|
||||||
|
limit = 50
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Satinalma (V3) tarafini sorgula - Simdilik V3 tarafi icin hammadde turu eslestirmesi belirsiz oldugundan pasif
|
||||||
|
/*
|
||||||
|
v3Sql := `
|
||||||
|
SELECT TOP (@p2)
|
||||||
|
'purchase' AS sourceType,
|
||||||
|
'BNZ' AS priceType,
|
||||||
|
CONVERT(VARCHAR(16), A.InvoiceDate, 120) AS Tarih,
|
||||||
|
ISNULL(A.InvoiceNumber, '') AS FaturaKodu,
|
||||||
|
LTRIM(RTRIM(ISNULL(A.ItemCode, ''))) AS MasrafKodu,
|
||||||
|
ISNULL(ID.ItemDescription, '') AS MasrafDetay,
|
||||||
|
ISNULL(A.ColorCode, '') AS ColorCode,
|
||||||
|
ISNULL(A.Qty1, 0) AS Miktar,
|
||||||
|
ISNULL(CI.UnitOfMeasureCode1, '') AS BIRIM,
|
||||||
|
ISNULL(A.Doc_Price, 0) AS EvrakFiyat,
|
||||||
|
ISNULL(A.Doc_CurrencyCode, '') AS EvrakDoviz
|
||||||
|
FROM AllInvoicesWithAttributes A
|
||||||
|
LEFT JOIN cdItem CI ON CI.ItemTypeCode = A.ItemTypeCode AND CI.ItemCode = A.ItemCode
|
||||||
|
OUTER APPLY (
|
||||||
|
SELECT TOP 1 ItemDescription FROM cdItemDesc
|
||||||
|
WHERE ItemTypeCode = A.ItemTypeCode AND ItemCode = A.ItemCode AND LangCode = 'TR'
|
||||||
|
) ID
|
||||||
|
WHERE A.ProcessCode IN ('BP')
|
||||||
|
AND A.ATAtt01 IN (1, 2)
|
||||||
|
AND A.CompanyCode IN (1, 2, 5)
|
||||||
|
AND A.IsCompleted = 1
|
||||||
|
AND YEAR(A.InvoiceDate) >= 2023
|
||||||
|
AND (NULLIF(@p1, '') IS NULL OR CONVERT(date, A.InvoiceDate) < CONVERT(date, NULLIF(@p1, ''), 23))
|
||||||
|
AND EXISTS (
|
||||||
|
-- Hammadde turu eslestirmesi (V3 tarafindaki karsiligi varsa)
|
||||||
|
SELECT 1 FROM cdItem WHERE ItemCode = A.ItemCode
|
||||||
|
)
|
||||||
|
ORDER BY A.InvoiceDate DESC
|
||||||
|
`
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 2. Uretim (Recete) tarafini sorgula
|
||||||
|
uretimSql := `
|
||||||
|
SELECT TOP (@p3)
|
||||||
|
'recipe' AS sourceType,
|
||||||
|
'BNZ' AS priceType,
|
||||||
|
CONVERT(VARCHAR(16), ISNULL(M.Tarihi, M.dteKayitTarihi), 120) AS dteIslemTarihi,
|
||||||
|
RTRIM(CONVERT(VARCHAR(32), ISNULL(D.nOnMLNo, 0))) AS nOnMLNo,
|
||||||
|
ISNULL(D.sKodu, '') AS sKodu,
|
||||||
|
ISNULL(D.sAciklama, '') AS sAciklama,
|
||||||
|
ISNULL(D.sRenk, '') AS sRenk,
|
||||||
|
ISNULL(D.lMiktar, 0) AS lMiktar,
|
||||||
|
ISNULL(D.sBirim, '') AS sBirim,
|
||||||
|
ISNULL(D.lDovizFiyati, 0) AS lDovizFiyati,
|
||||||
|
CASE
|
||||||
|
WHEN LTRIM(RTRIM(ISNULL(D.fiyat_doviz, ''))) <> '' THEN LTRIM(RTRIM(D.fiyat_doviz))
|
||||||
|
WHEN LTRIM(RTRIM(ISNULL(M.sDovizCinsi, ''))) <> '' THEN LTRIM(RTRIM(M.sDovizCinsi))
|
||||||
|
ELSE 'USD'
|
||||||
|
END AS USD
|
||||||
|
FROM dbo.spUrtOnMLMasDet D
|
||||||
|
INNER JOIN dbo.spUrtOnMLMas M ON M.nOnMLNo = D.nOnMLNo
|
||||||
|
WHERE D.nHammaddeTuruNo = @p1
|
||||||
|
AND (NULLIF(@p2, '') IS NULL OR CONVERT(date, ISNULL(M.Tarihi, M.dteKayitTarihi)) < CONVERT(date, NULLIF(@p2, ''), 23))
|
||||||
|
ORDER BY ISNULL(M.Tarihi, M.dteKayitTarihi) DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
// Not: nHammaddeTuruNo parametresine gore sadece Reçete tarafı şu an doğrudan filtrelenebiliyor.
|
||||||
|
// V3 tarafı için ItemTypeCode veya özel bir grup kodu gerekebilir.
|
||||||
|
// Kullanıcının talebi üzerine Reçete (URETIM) odaklı sorguyu önceliklendiriyoruz.
|
||||||
|
|
||||||
|
rows, err := uretimDB.QueryContext(ctx, uretimSql, nHammaddeTuruNo, costDate, limit)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var results []any
|
||||||
|
for rows.Next() {
|
||||||
|
// Basit bir map veya struct ile dondurebiliriz
|
||||||
|
var row struct {
|
||||||
|
SourceType string `json:"sourceType"`
|
||||||
|
PriceType string `json:"priceType"`
|
||||||
|
Tarih string `json:"Tarih"`
|
||||||
|
EvrakKodu string `json:"EvrakKodu"`
|
||||||
|
Kod string `json:"Kod"`
|
||||||
|
Aciklama string `json:"Aciklama"`
|
||||||
|
Renk string `json:"Renk"`
|
||||||
|
Miktar float64 `json:"Miktar"`
|
||||||
|
Birim string `json:"Birim"`
|
||||||
|
Fiyat float64 `json:"Fiyat"`
|
||||||
|
Doviz string `json:"Doviz"`
|
||||||
|
}
|
||||||
|
if err := rows.Scan(
|
||||||
|
&row.SourceType, &row.PriceType, &row.Tarih, &row.EvrakKodu,
|
||||||
|
&row.Kod, &row.Aciklama, &row.Renk, &row.Miktar, &row.Birim,
|
||||||
|
&row.Fiyat, &row.Doviz,
|
||||||
|
); err == nil {
|
||||||
|
results = append(results, row)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
@@ -760,7 +760,16 @@ func getOrderLinesFromDB(db *sql.DB, orderID string) ([]OrderLineRaw, error) {
|
|||||||
P.ProductAtt01Desc,
|
P.ProductAtt01Desc,
|
||||||
P.ProductAtt02Desc,
|
P.ProductAtt02Desc,
|
||||||
P.ProductAtt44Desc,
|
P.ProductAtt44Desc,
|
||||||
L.IsClosed,
|
CASE
|
||||||
|
WHEN ISNULL(L.IsClosed, 0) = 1
|
||||||
|
OR EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM BAGGI_V3.dbo.trInvoiceLine il WITH (NOLOCK)
|
||||||
|
WHERE il.OrderLineID = L.OrderLineID
|
||||||
|
)
|
||||||
|
THEN CAST(1 AS bit)
|
||||||
|
ELSE CAST(0 AS bit)
|
||||||
|
END AS IsClosed,
|
||||||
L.WithHoldingTaxTypeCode,
|
L.WithHoldingTaxTypeCode,
|
||||||
L.DOVCode,
|
L.DOVCode,
|
||||||
L.PlannedDateOfLading,
|
L.PlannedDateOfLading,
|
||||||
|
|||||||
2048
svc/routes/production_product_costing.go
Normal file
2048
svc/routes/production_product_costing.go
Normal file
@@ -0,0 +1,2048 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bssapp-backend/auth"
|
||||||
|
"bssapp-backend/db"
|
||||||
|
"bssapp-backend/models"
|
||||||
|
"bssapp-backend/queries"
|
||||||
|
"bssapp-backend/utils"
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func logProductionHasCostDetailEditorOptionItemDiagnostics(ctx context.Context, uretimDB *sql.DB, search string, limit int) {
|
||||||
|
logger := utils.SlogFromContext(ctx).With(
|
||||||
|
"handler", "production-product-costing.detail-editor-options.diagnostics",
|
||||||
|
"search", search,
|
||||||
|
"limit", limit,
|
||||||
|
)
|
||||||
|
expectedColumns := []string{
|
||||||
|
"nStokID",
|
||||||
|
"sKodu",
|
||||||
|
"sAciklama",
|
||||||
|
"sModel",
|
||||||
|
"sBirimCinsi1",
|
||||||
|
"IsBlocked",
|
||||||
|
}
|
||||||
|
probes := []struct {
|
||||||
|
name string
|
||||||
|
sqlText string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "projection:nStokID",
|
||||||
|
sqlText: `SELECT TOP 1 RTRIM(CONVERT(VARCHAR(32), ISNULL(S.nStokID, 0))) FROM dbo.tbStok S`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "projection:sKodu",
|
||||||
|
sqlText: `SELECT TOP 1 LTRIM(RTRIM(CONVERT(NVARCHAR(255), ISNULL(S.sKodu, '')))) FROM dbo.tbStok S`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "projection:sAciklama",
|
||||||
|
sqlText: `SELECT TOP 1 LTRIM(RTRIM(CONVERT(NVARCHAR(255), ISNULL(S.sAciklama, '')))) FROM dbo.tbStok S`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "projection:sModel",
|
||||||
|
sqlText: `SELECT TOP 1 LTRIM(RTRIM(CONVERT(NVARCHAR(255), ISNULL(S.sModel, '')))) FROM dbo.tbStok S`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "projection:sBirimCinsi1",
|
||||||
|
sqlText: `SELECT TOP 1 LTRIM(RTRIM(CONVERT(NVARCHAR(64), ISNULL(S.sBirimCinsi1, '')))) FROM dbo.tbStok S`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "projection:IsBlocked",
|
||||||
|
sqlText: `SELECT TOP 1 RTRIM(CONVERT(VARCHAR(32), ISNULL(S.IsBlocked, 0))) FROM dbo.tbStok S`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "filter:model_like_count",
|
||||||
|
sqlText: `SELECT RTRIM(CONVERT(VARCHAR(32), COUNT(1))) FROM dbo.tbStok S WHERE LTRIM(RTRIM(CONVERT(NVARCHAR(255), ISNULL(S.sModel, '')))) LIKE '_.%'`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "filter:isblocked_count",
|
||||||
|
sqlText: `SELECT RTRIM(CONVERT(VARCHAR(32), COUNT(1))) FROM dbo.tbStok S WHERE ISNULL(S.IsBlocked, 0) = 0`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "filter:final_count",
|
||||||
|
sqlText: `SELECT RTRIM(CONVERT(VARCHAR(32), COUNT(1))) FROM dbo.tbStok S WHERE ISNULL(S.IsBlocked, 0) = 0 AND LTRIM(RTRIM(CONVERT(NVARCHAR(255), ISNULL(S.sModel, '')))) LIKE '_.%'`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[ProductionHasCostDetailEditorOptions] item diagnostics start search=%q limit=%d", search, limit)
|
||||||
|
|
||||||
|
for _, columnName := range expectedColumns {
|
||||||
|
var exists int
|
||||||
|
err := uretimDB.QueryRowContext(
|
||||||
|
ctx,
|
||||||
|
`SELECT COUNT(1)
|
||||||
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = 'dbo'
|
||||||
|
AND TABLE_NAME = 'tbStok'
|
||||||
|
AND COLUMN_NAME = @p1`,
|
||||||
|
columnName,
|
||||||
|
).Scan(&exists)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("query error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailEditorOptions] item diagnostics column check error column=%s err=%v", columnName, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Printf("[ProductionHasCostDetailEditorOptions] item diagnostics column=%s exists=%t", columnName, exists > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, probe := range probes {
|
||||||
|
var sample sql.NullString
|
||||||
|
err := uretimDB.QueryRowContext(ctx, probe.sqlText).Scan(&sample)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailEditorOptions] item diagnostics probe=%s err=%v", probe.name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Printf("[ProductionHasCostDetailEditorOptions] item diagnostics probe=%s sample=%q", probe.name, strings.TrimSpace(sample.String))
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[ProductionHasCostDetailEditorOptions] item diagnostics end search=%q limit=%d", search, limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/pricing/production-product-costing/no-cost-products
|
||||||
|
func GetProductionNoCostProductsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
uretimDB := db.GetUretimDB()
|
||||||
|
if uretimDB == nil {
|
||||||
|
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
search := strings.TrimSpace(r.URL.Query().Get("search"))
|
||||||
|
fromDate := strings.TrimSpace(r.URL.Query().Get("from_date"))
|
||||||
|
|
||||||
|
rows, err := queries.GetProductionNoCostProducts(r.Context(), uretimDB, fromDate, search)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("❌ [ProductionNoCost] query error: %v", err)
|
||||||
|
http.Error(w, "Veritabanı hatası", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
list := make([]models.ProductionNoCostProductRow, 0, 200)
|
||||||
|
for rows.Next() {
|
||||||
|
var item models.ProductionNoCostProductRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&item.UretimSekli,
|
||||||
|
&item.UrtSiparisNo,
|
||||||
|
&item.IslemTarihi,
|
||||||
|
&item.FirmaKodu,
|
||||||
|
&item.FirmaAdi,
|
||||||
|
&item.SonIsEmriVeren,
|
||||||
|
&item.MMiktarG,
|
||||||
|
&item.MModelKodu,
|
||||||
|
&item.Kodu,
|
||||||
|
&item.ModelAdi,
|
||||||
|
&item.SKullaniciAdi,
|
||||||
|
&item.SKullaniciGunc,
|
||||||
|
); err != nil {
|
||||||
|
log.Printf("⚠️ [ProductionNoCost] scan error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
list = append(list, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
log.Printf("⚠️ [ProductionNoCost] rows error: %v", err)
|
||||||
|
http.Error(w, "Veritabanı satır hatası", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = json.NewEncoder(w).Encode(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/pricing/production-product-costing/has-cost-products
|
||||||
|
func GetProductionHasCostProductsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
uretimDB := db.GetUretimDB()
|
||||||
|
if uretimDB == nil {
|
||||||
|
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
search := strings.TrimSpace(r.URL.Query().Get("search"))
|
||||||
|
offset := parsePositiveIntOrDefault(r.URL.Query().Get("offset"), 0)
|
||||||
|
limit := parsePositiveIntOrDefault(r.URL.Query().Get("limit"), 300)
|
||||||
|
if limit > 1000 {
|
||||||
|
limit = 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := queries.GetProductionHasCostProducts(r.Context(), uretimDB, search, offset, limit)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("❌ [ProductionHasCost] query error: %v", err)
|
||||||
|
http.Error(w, "Veritabanı hatası", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
list := make([]models.ProductionHasCostProductRow, 0, 200)
|
||||||
|
for rows.Next() {
|
||||||
|
var item models.ProductionHasCostProductRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&item.UretimSekli,
|
||||||
|
&item.NOnMLNo,
|
||||||
|
&item.UrunKodu,
|
||||||
|
&item.UrunAdi,
|
||||||
|
&item.Tarihi,
|
||||||
|
&item.DteKayitTarihi,
|
||||||
|
&item.SKullaniciAdi,
|
||||||
|
&item.LTutarTL,
|
||||||
|
&item.LTutarUSD,
|
||||||
|
&item.LTutarEURO,
|
||||||
|
&item.DteGuncellemeTarihi,
|
||||||
|
&item.SGuncellemeKullaniciAdi,
|
||||||
|
&item.NUrtReceteID,
|
||||||
|
&item.SAciklama,
|
||||||
|
&item.SonSiparisTarihi,
|
||||||
|
&item.MaliyetDurumu,
|
||||||
|
); err != nil {
|
||||||
|
log.Printf("⚠️ [ProductionHasCost] scan error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
list = append(list, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
log.Printf("⚠️ [ProductionHasCost] rows error: %v", err)
|
||||||
|
http.Error(w, "Veritabanı satır hatası", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = json.NewEncoder(w).Encode(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/pricing/production-product-costing/has-cost-history
|
||||||
|
func GetProductionHasCostHistoryHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
uretimDB := db.GetUretimDB()
|
||||||
|
if uretimDB == nil {
|
||||||
|
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
productCode := strings.TrimSpace(r.URL.Query().Get("urun_kodu"))
|
||||||
|
if productCode == "" {
|
||||||
|
http.Error(w, "urun_kodu zorunlu", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := queries.GetProductionHasCostHistoryByProductCode(r.Context(), uretimDB, productCode)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("⌠[ProductionHasCostHistory] query error: %v", err)
|
||||||
|
http.Error(w, "Veritabanı hatası", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
list := make([]models.ProductionHasCostHistoryRow, 0, 100)
|
||||||
|
for rows.Next() {
|
||||||
|
var item models.ProductionHasCostHistoryRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&item.NOnMLNo,
|
||||||
|
&item.UrunKodu,
|
||||||
|
&item.UrunAdi,
|
||||||
|
&item.Tarihi,
|
||||||
|
&item.SKullaniciAdi,
|
||||||
|
&item.LTutarUSD,
|
||||||
|
&item.LTutarTL,
|
||||||
|
&item.LTutarEURO,
|
||||||
|
&item.SDovizCinsi,
|
||||||
|
&item.LTutarDoviz,
|
||||||
|
&item.DteGuncellemeTarihi,
|
||||||
|
&item.SGuncellemeKullaniciAdi,
|
||||||
|
&item.NUrtReceteID,
|
||||||
|
&item.SAciklama,
|
||||||
|
); err != nil {
|
||||||
|
log.Printf("âš ï¸ [ProductionHasCostHistory] scan error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
list = append(list, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
log.Printf("âš ï¸ [ProductionHasCostHistory] rows error: %v", err)
|
||||||
|
http.Error(w, "Veritabanı satır hatası", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = json.NewEncoder(w).Encode(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/pricing/production-product-costing/has-cost-detail-groups
|
||||||
|
func GetProductionHasCostDetailGroupsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
uretimDB := db.GetUretimDB()
|
||||||
|
if uretimDB == nil {
|
||||||
|
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
detailSource := strings.ToLower(strings.TrimSpace(r.URL.Query().Get("detail_source")))
|
||||||
|
recipeCode := strings.TrimSpace(r.URL.Query().Get("recete_kodu"))
|
||||||
|
productCode := strings.TrimSpace(r.URL.Query().Get("urun_kodu"))
|
||||||
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
|
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||||
|
logger := utils.SlogFromContext(ctx).With(
|
||||||
|
"handler", "production-product-costing.detail-groups",
|
||||||
|
"detail_source", detailSource,
|
||||||
|
"urun_kodu", productCode,
|
||||||
|
"recete_kodu", recipeCode,
|
||||||
|
)
|
||||||
|
|
||||||
|
if detailSource == "no-cost" || recipeCode != "" {
|
||||||
|
if recipeCode == "" {
|
||||||
|
logger.Warn("request invalid", "reason", "missing recete_kodu")
|
||||||
|
http.Error(w, "recete_kodu zorunlu", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Info("request start")
|
||||||
|
|
||||||
|
rows, err := queries.GetProductionNoCostDetailRowsByRecipeCode(ctx, uretimDB, recipeCode, productCode)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("query error", "err", err)
|
||||||
|
log.Printf("❌ [ProductionNoCostDetailGroups] query error: %v", err)
|
||||||
|
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
groups := make([]models.ProductionHasCostDetailGroup, 0, 16)
|
||||||
|
groupIndexByName := map[string]int{}
|
||||||
|
scannedRows := 0
|
||||||
|
scanErrors := 0
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var (
|
||||||
|
groupName string
|
||||||
|
groupTotal float64
|
||||||
|
groupTotalUSD float64
|
||||||
|
fiyatGirilen sql.NullFloat64
|
||||||
|
fiyatDoviz sql.NullString
|
||||||
|
maliyeteDahil sql.NullBool
|
||||||
|
cmPriceTypeID sql.NullInt64
|
||||||
|
item models.ProductionHasCostDetailGroupItem
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := rows.Scan(
|
||||||
|
&groupName,
|
||||||
|
&groupTotal,
|
||||||
|
&groupTotalUSD,
|
||||||
|
&item.NOnMLNo,
|
||||||
|
&item.NOnMLDetNo,
|
||||||
|
&item.NHammaddeTuruNo,
|
||||||
|
&item.SKodu,
|
||||||
|
&item.SAciklama,
|
||||||
|
&item.SRenk,
|
||||||
|
&item.SBeden,
|
||||||
|
&item.SAciklama2,
|
||||||
|
&item.LMiktar,
|
||||||
|
&item.LFiyat,
|
||||||
|
&item.LTutar,
|
||||||
|
&item.SFiyatTipi,
|
||||||
|
&item.SDovizCinsi,
|
||||||
|
&item.LDovizKuru,
|
||||||
|
&item.LDovizFiyati,
|
||||||
|
&fiyatGirilen,
|
||||||
|
&fiyatDoviz,
|
||||||
|
&maliyeteDahil,
|
||||||
|
&cmPriceTypeID,
|
||||||
|
&item.USDTutar,
|
||||||
|
&item.EURTutar,
|
||||||
|
&item.GBPTutar,
|
||||||
|
&item.SBirim,
|
||||||
|
&item.SHammaddeTuruAdi,
|
||||||
|
&item.SParcaAdi,
|
||||||
|
); err != nil {
|
||||||
|
scanErrors += 1
|
||||||
|
logger.Warn("scan error", "scan_index", scannedRows+scanErrors+1, "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionNoCostDetailGroups] scan error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
scannedRows += 1
|
||||||
|
|
||||||
|
if fiyatGirilen.Valid {
|
||||||
|
item.FiyatGirilen = new(float64)
|
||||||
|
*item.FiyatGirilen = fiyatGirilen.Float64
|
||||||
|
}
|
||||||
|
if fiyatDoviz.Valid {
|
||||||
|
item.FiyatDoviz = strings.TrimSpace(fiyatDoviz.String)
|
||||||
|
}
|
||||||
|
item.MaliyeteDahil = !maliyeteDahil.Valid || maliyeteDahil.Bool
|
||||||
|
if cmPriceTypeID.Valid {
|
||||||
|
value := int(cmPriceTypeID.Int64)
|
||||||
|
item.CMPriceTypeID = &value
|
||||||
|
}
|
||||||
|
|
||||||
|
idx, ok := groupIndexByName[groupName]
|
||||||
|
if !ok {
|
||||||
|
groups = append(groups, models.ProductionHasCostDetailGroup{
|
||||||
|
SAciklama3: groupName,
|
||||||
|
TotalTutar: groupTotal,
|
||||||
|
TotalUSDTutar: groupTotalUSD,
|
||||||
|
Items: make([]models.ProductionHasCostDetailGroupItem, 0, 8),
|
||||||
|
})
|
||||||
|
idx = len(groups) - 1
|
||||||
|
groupIndexByName[groupName] = idx
|
||||||
|
}
|
||||||
|
|
||||||
|
groups[idx].Items = append(groups[idx].Items, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
logger.Error("rows error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionNoCostDetailGroups] rows error: %v", err)
|
||||||
|
http.Error(w, "Veritabani satir hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("request done", "group_count", len(groups), "row_count", scannedRows, "scan_errors", scanErrors)
|
||||||
|
log.Printf("[ProductionNoCostDetailGroups] done recete_kodu=%s groups=%d", recipeCode, len(groups))
|
||||||
|
_ = json.NewEncoder(w).Encode(groups)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rawOnMLNo := strings.TrimSpace(r.URL.Query().Get("n_onml_no"))
|
||||||
|
nOnMLNo, err := strconv.Atoi(rawOnMLNo)
|
||||||
|
if err != nil || nOnMLNo <= 0 {
|
||||||
|
logger.Warn("request invalid", "reason", "invalid n_onml_no", "raw_n_onml_no", rawOnMLNo)
|
||||||
|
http.Error(w, "n_onml_no zorunlu ve pozitif sayi olmali", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger = logger.With("n_onml_no", nOnMLNo)
|
||||||
|
logger.Info("request start")
|
||||||
|
log.Printf("[ProductionHasCostDetailGroups] start n_onml_no=%d", nOnMLNo)
|
||||||
|
|
||||||
|
rows, err := queries.GetProductionHasCostDetailRowsByOnMLNo(ctx, uretimDB, nOnMLNo)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("query error", "err", err)
|
||||||
|
log.Printf("⌠[ProductionHasCostDetailGroups] query error: %v", err)
|
||||||
|
http.Error(w, "Veritabanı hatası", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
groups := make([]models.ProductionHasCostDetailGroup, 0, 16)
|
||||||
|
groupIndexByName := map[string]int{}
|
||||||
|
scannedRows := 0
|
||||||
|
scanErrors := 0
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var (
|
||||||
|
groupName string
|
||||||
|
groupTotal float64
|
||||||
|
groupTotalUSD float64
|
||||||
|
fiyatGirilen sql.NullFloat64
|
||||||
|
fiyatDoviz sql.NullString
|
||||||
|
maliyeteDahil sql.NullBool
|
||||||
|
cmPriceTypeID sql.NullInt64
|
||||||
|
item models.ProductionHasCostDetailGroupItem
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := rows.Scan(
|
||||||
|
&groupName,
|
||||||
|
&groupTotal,
|
||||||
|
&groupTotalUSD,
|
||||||
|
&item.NOnMLNo,
|
||||||
|
&item.NOnMLDetNo,
|
||||||
|
&item.NHammaddeTuruNo,
|
||||||
|
&item.SKodu,
|
||||||
|
&item.SAciklama,
|
||||||
|
&item.SRenk,
|
||||||
|
&item.SBeden,
|
||||||
|
&item.SAciklama2,
|
||||||
|
&item.LMiktar,
|
||||||
|
&item.LFiyat,
|
||||||
|
&item.LTutar,
|
||||||
|
&item.SFiyatTipi,
|
||||||
|
&item.SDovizCinsi,
|
||||||
|
&item.LDovizKuru,
|
||||||
|
&item.LDovizFiyati,
|
||||||
|
&fiyatGirilen,
|
||||||
|
&fiyatDoviz,
|
||||||
|
&maliyeteDahil,
|
||||||
|
&cmPriceTypeID,
|
||||||
|
&item.USDTutar,
|
||||||
|
&item.EURTutar,
|
||||||
|
&item.GBPTutar,
|
||||||
|
&item.SBirim,
|
||||||
|
&item.SHammaddeTuruAdi,
|
||||||
|
&item.SParcaAdi,
|
||||||
|
); err != nil {
|
||||||
|
log.Printf("âš ï¸ [ProductionHasCostDetailGroups] scan error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
scannedRows += 1
|
||||||
|
|
||||||
|
if fiyatGirilen.Valid {
|
||||||
|
item.FiyatGirilen = new(float64)
|
||||||
|
*item.FiyatGirilen = fiyatGirilen.Float64
|
||||||
|
}
|
||||||
|
if fiyatDoviz.Valid {
|
||||||
|
item.FiyatDoviz = strings.TrimSpace(fiyatDoviz.String)
|
||||||
|
}
|
||||||
|
item.MaliyeteDahil = maliyeteDahil.Valid && maliyeteDahil.Bool
|
||||||
|
if cmPriceTypeID.Valid {
|
||||||
|
value := int(cmPriceTypeID.Int64)
|
||||||
|
item.CMPriceTypeID = &value
|
||||||
|
}
|
||||||
|
|
||||||
|
idx, ok := groupIndexByName[groupName]
|
||||||
|
if !ok {
|
||||||
|
groups = append(groups, models.ProductionHasCostDetailGroup{
|
||||||
|
SAciklama3: groupName,
|
||||||
|
TotalTutar: groupTotal,
|
||||||
|
TotalUSDTutar: groupTotalUSD,
|
||||||
|
Items: make([]models.ProductionHasCostDetailGroupItem, 0, 24),
|
||||||
|
})
|
||||||
|
idx = len(groups) - 1
|
||||||
|
groupIndexByName[groupName] = idx
|
||||||
|
}
|
||||||
|
|
||||||
|
groups[idx].Items = append(groups[idx].Items, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
log.Printf("âš ï¸ [ProductionHasCostDetailGroups] rows error: %v", err)
|
||||||
|
http.Error(w, "Veritabanı satır hatası", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("request done", "group_count", len(groups), "row_count", scannedRows, "scan_errors", scanErrors)
|
||||||
|
log.Printf("[ProductionHasCostDetailGroups] done n_onml_no=%d groups=%d rows=%d scan_errors=%d", nOnMLNo, len(groups), scannedRows, scanErrors)
|
||||||
|
_ = json.NewEncoder(w).Encode(groups)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/pricing/production-product-costing/has-cost-detail-header
|
||||||
|
func GetProductionHasCostDetailHeaderHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
uretimDB := db.GetUretimDB()
|
||||||
|
if uretimDB == nil {
|
||||||
|
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
mssqlDB := db.GetDB()
|
||||||
|
|
||||||
|
detailSource := strings.ToLower(strings.TrimSpace(r.URL.Query().Get("detail_source")))
|
||||||
|
recipeCode := strings.TrimSpace(r.URL.Query().Get("recete_kodu"))
|
||||||
|
productCode := strings.TrimSpace(r.URL.Query().Get("urun_kodu"))
|
||||||
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
|
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||||
|
logger := utils.SlogFromContext(ctx).With(
|
||||||
|
"handler", "production-product-costing.detail-header",
|
||||||
|
"detail_source", detailSource,
|
||||||
|
"urun_kodu", productCode,
|
||||||
|
"recete_kodu", recipeCode,
|
||||||
|
)
|
||||||
|
|
||||||
|
if detailSource == "no-cost" || recipeCode != "" {
|
||||||
|
if recipeCode == "" {
|
||||||
|
logger.Warn("request invalid", "reason", "missing recete_kodu")
|
||||||
|
http.Error(w, "recete_kodu zorunlu", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Info("request start")
|
||||||
|
|
||||||
|
row, err := queries.GetProductionNoCostDetailHeaderByRecipeCode(ctx, uretimDB, recipeCode, productCode)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("query prepare error", "err", err)
|
||||||
|
log.Printf("❌ [ProductionNoCostDetailHeader] query prepare error: %v", err)
|
||||||
|
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var item models.ProductionHasCostDetailHeader
|
||||||
|
if err := row.Scan(
|
||||||
|
&item.UretimiYapanFirma,
|
||||||
|
&item.SonIsEmriVeren,
|
||||||
|
&item.NOnMLNo,
|
||||||
|
&item.UrunKodu,
|
||||||
|
&item.UrunAdi,
|
||||||
|
&item.UretimSekliID,
|
||||||
|
&item.UretimSekli,
|
||||||
|
&item.DteKayitTarihi,
|
||||||
|
&item.SKullaniciAdi,
|
||||||
|
&item.LTutarTL,
|
||||||
|
&item.LTutarUSD,
|
||||||
|
&item.LTutarEURO,
|
||||||
|
&item.LTutarGBP,
|
||||||
|
&item.SDovizCinsi,
|
||||||
|
&item.LTutarDoviz,
|
||||||
|
&item.DteGuncellemeTarihi,
|
||||||
|
&item.SGuncellemeKullaniciAdi,
|
||||||
|
&item.NUrtReceteID,
|
||||||
|
); err != nil {
|
||||||
|
logger.Warn("scan or not found", "err", err)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
logger.Warn("row not found")
|
||||||
|
http.Error(w, "Kayit bulunamadi", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("❌ [ProductionNoCostDetailHeader] scan error: %v", err)
|
||||||
|
http.Error(w, "Veritabani satir hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("request done", "n_urt_recete_id", item.NUrtReceteID, "urun_kodu", item.UrunKodu)
|
||||||
|
if mssqlDB != nil {
|
||||||
|
ana, alt, err := queries.GetProductAnaAltGrupByUrunKodu(ctx, mssqlDB, item.UrunKodu)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("product group query error", "err", err)
|
||||||
|
} else {
|
||||||
|
item.UrunAnaGrubu = ana
|
||||||
|
item.UrunAltGrubu = alt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = json.NewEncoder(w).Encode(item)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rawOnMLNo := strings.TrimSpace(r.URL.Query().Get("n_onml_no"))
|
||||||
|
nOnMLNo, err := strconv.Atoi(rawOnMLNo)
|
||||||
|
if err != nil || nOnMLNo <= 0 {
|
||||||
|
logger.Warn("request invalid", "reason", "invalid n_onml_no", "raw_n_onml_no", rawOnMLNo)
|
||||||
|
http.Error(w, "n_onml_no zorunlu ve pozitif sayi olmali", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger = logger.With("n_onml_no", nOnMLNo)
|
||||||
|
logger.Info("request start")
|
||||||
|
|
||||||
|
row, err := queries.GetProductionHasCostDetailHeaderByOnMLNo(ctx, uretimDB, nOnMLNo)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("query prepare error", "err", err)
|
||||||
|
log.Printf("❌ [ProductionHasCostDetailHeader] query prepare error: %v", err)
|
||||||
|
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var item models.ProductionHasCostDetailHeader
|
||||||
|
if err := row.Scan(
|
||||||
|
&item.UretimiYapanFirma,
|
||||||
|
&item.SonIsEmriVeren,
|
||||||
|
&item.NOnMLNo,
|
||||||
|
&item.UrunKodu,
|
||||||
|
&item.UrunAdi,
|
||||||
|
&item.UretimSekliID,
|
||||||
|
&item.UretimSekli,
|
||||||
|
&item.DteKayitTarihi,
|
||||||
|
&item.SKullaniciAdi,
|
||||||
|
&item.LTutarTL,
|
||||||
|
&item.LTutarUSD,
|
||||||
|
&item.LTutarEURO,
|
||||||
|
&item.LTutarGBP,
|
||||||
|
&item.SDovizCinsi,
|
||||||
|
&item.LTutarDoviz,
|
||||||
|
&item.DteGuncellemeTarihi,
|
||||||
|
&item.SGuncellemeKullaniciAdi,
|
||||||
|
&item.NUrtReceteID,
|
||||||
|
); err != nil {
|
||||||
|
logger.Warn("scan or not found", "err", err)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
http.Error(w, "Kayit bulunamadi", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("❌ [ProductionHasCostDetailHeader] scan error: %v", err)
|
||||||
|
http.Error(w, "Veritabani satir hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("request done", "n_onml_no", item.NOnMLNo, "urun_kodu", item.UrunKodu, "n_urt_recete_id", item.NUrtReceteID)
|
||||||
|
if mssqlDB != nil {
|
||||||
|
ana, alt, err := queries.GetProductAnaAltGrupByUrunKodu(ctx, mssqlDB, item.UrunKodu)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("product group query error", "err", err)
|
||||||
|
} else {
|
||||||
|
item.UrunAnaGrubu = ana
|
||||||
|
item.UrunAltGrubu = alt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ = json.NewEncoder(w).Encode(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/pricing/production-product-costing/production-types
|
||||||
|
func GetProductionTypesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
uretimDB := db.GetUretimDB()
|
||||||
|
if uretimDB == nil {
|
||||||
|
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
|
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||||
|
logger := utils.SlogFromContext(ctx).With("handler", "production-product-costing.production-types")
|
||||||
|
logger.Info("request start")
|
||||||
|
|
||||||
|
rows, err := queries.GetProductionTypes(ctx, uretimDB)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("query error", "err", err)
|
||||||
|
log.Printf("❌ [ProductionTypes] query error: %v", err)
|
||||||
|
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
var types []models.ProductionType
|
||||||
|
for rows.Next() {
|
||||||
|
var t models.ProductionType
|
||||||
|
if err := rows.Scan(&t.ID, &t.Aciklama); err != nil {
|
||||||
|
logger.Warn("scan error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionTypes] scan error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
types = append(types, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
logger.Error("rows error", "err", err)
|
||||||
|
http.Error(w, "Veritabani satir hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("request done", "row_count", len(types))
|
||||||
|
_ = json.NewEncoder(w).Encode(types)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/pricing/production-product-costing/detail-editor-options
|
||||||
|
func GetProductionHasCostDetailEditorOptionsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
|
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||||
|
kind := strings.ToLower(strings.TrimSpace(r.URL.Query().Get("kind")))
|
||||||
|
search := strings.TrimSpace(r.URL.Query().Get("search"))
|
||||||
|
limit := parsePositiveIntOrDefault(r.URL.Query().Get("limit"), 100)
|
||||||
|
if limit > 200 {
|
||||||
|
limit = 200
|
||||||
|
}
|
||||||
|
logger := utils.SlogFromContext(ctx).With(
|
||||||
|
"handler", "production-product-costing.detail-editor-options",
|
||||||
|
"kind", kind,
|
||||||
|
"search", search,
|
||||||
|
"limit", limit,
|
||||||
|
)
|
||||||
|
logger.Info("request start")
|
||||||
|
|
||||||
|
switch kind {
|
||||||
|
case "hammadde":
|
||||||
|
uretimDB := db.GetUretimDB()
|
||||||
|
if uretimDB == nil {
|
||||||
|
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := queries.GetProductionHasCostDetailHammaddeTypeOptions(ctx, uretimDB, search, limit)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("hammadde query error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailEditorOptions] hammadde query error: %v", err)
|
||||||
|
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
if columns, columnErr := rows.Columns(); columnErr != nil {
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailEditorOptions] item columns read error search=%q limit=%d err=%v", search, limit, columnErr)
|
||||||
|
} else {
|
||||||
|
log.Printf("[ProductionHasCostDetailEditorOptions] item columns=%v", columns)
|
||||||
|
}
|
||||||
|
|
||||||
|
list := make([]models.ProductionHasCostDetailEditorOption, 0, limit)
|
||||||
|
for rows.Next() {
|
||||||
|
var item models.ProductionHasCostDetailEditorOption
|
||||||
|
if err := rows.Scan(&item.NHammaddeTuruNo, &item.SHammaddeTuruAdi, &item.SAciklama3); err != nil {
|
||||||
|
logger.Warn("hammadde scan error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailEditorOptions] hammadde scan error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
item.Kind = "hammadde"
|
||||||
|
item.Value = item.NHammaddeTuruNo
|
||||||
|
item.Label = strings.TrimSpace(item.NHammaddeTuruNo + " - " + item.SHammaddeTuruAdi)
|
||||||
|
item.SParcaAdi = item.SAciklama3
|
||||||
|
list = append(list, item)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
logger.Error("hammadde rows error", "err", err)
|
||||||
|
http.Error(w, "Veritabani satir hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Info("request done", "kind", "hammadde", "row_count", len(list))
|
||||||
|
_ = json.NewEncoder(w).Encode(list)
|
||||||
|
return
|
||||||
|
|
||||||
|
case "item":
|
||||||
|
uretimDB := db.GetUretimDB()
|
||||||
|
if uretimDB == nil {
|
||||||
|
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[ProductionHasCostDetailEditorOptions] item start search=%q limit=%d", search, limit)
|
||||||
|
rows, err := queries.GetProductionHasCostDetailItemOptions(ctx, uretimDB, search, limit)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("item query error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailEditorOptions] item query error search=%q limit=%d err=%v", search, limit, err)
|
||||||
|
logProductionHasCostDetailEditorOptionItemDiagnostics(ctx, uretimDB, search, limit)
|
||||||
|
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
if columns, columnErr := rows.Columns(); columnErr != nil {
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailEditorOptions] item columns read error search=%q limit=%d err=%v", search, limit, columnErr)
|
||||||
|
} else {
|
||||||
|
log.Printf("[ProductionHasCostDetailEditorOptions] item columns=%v", columns)
|
||||||
|
}
|
||||||
|
|
||||||
|
list := make([]models.ProductionHasCostDetailEditorOption, 0, limit)
|
||||||
|
scanErrors := 0
|
||||||
|
for rows.Next() {
|
||||||
|
var item models.ProductionHasCostDetailEditorOption
|
||||||
|
if err := rows.Scan(&item.NStokID, &item.SKodu, &item.SAciklama, &item.SModel, &item.SBirim); err != nil {
|
||||||
|
scanErrors += 1
|
||||||
|
logger.Warn("item scan error", "scan_index", len(list)+scanErrors, "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailEditorOptions] item scan error scan_index=%d err=%v", len(list)+scanErrors, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
item.Kind = "item"
|
||||||
|
item.Value = item.SKodu
|
||||||
|
item.Label = strings.TrimSpace(item.SKodu + " - " + item.SAciklama)
|
||||||
|
list = append(list, item)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
logger.Error("item rows error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailEditorOptions] item rows error search=%q limit=%d err=%v", search, limit, err)
|
||||||
|
http.Error(w, "Veritabani satir hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Info("request done", "kind", "item", "row_count", len(list), "scan_errors", scanErrors)
|
||||||
|
log.Printf("[ProductionHasCostDetailEditorOptions] item done search=%q limit=%d row_count=%d scan_errors=%d", search, limit, len(list), scanErrors)
|
||||||
|
_ = json.NewEncoder(w).Encode(list)
|
||||||
|
return
|
||||||
|
|
||||||
|
case "color":
|
||||||
|
uretimDB := db.GetUretimDB()
|
||||||
|
if uretimDB == nil {
|
||||||
|
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
modelCode := strings.TrimSpace(r.URL.Query().Get("model_code"))
|
||||||
|
if modelCode == "" {
|
||||||
|
_ = json.NewEncoder(w).Encode([]models.ProductionHasCostDetailEditorOption{})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := queries.GetProductionHasCostDetailColorOptions(ctx, uretimDB, modelCode, search, limit)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("color query error", "model_code", modelCode, "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailEditorOptions] color query error: %v", err)
|
||||||
|
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
list := make([]models.ProductionHasCostDetailEditorOption, 0, limit)
|
||||||
|
for rows.Next() {
|
||||||
|
var item models.ProductionHasCostDetailEditorOption
|
||||||
|
if err := rows.Scan(&item.ColorCode, &item.ColorDescription); err != nil {
|
||||||
|
logger.Warn("color scan error", "model_code", modelCode, "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailEditorOptions] color scan error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
item.Kind = "color"
|
||||||
|
item.Value = item.ColorCode
|
||||||
|
item.Label = strings.TrimSpace(item.ColorCode + " - " + item.ColorDescription)
|
||||||
|
list = append(list, item)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
logger.Error("color rows error", "model_code", modelCode, "err", err)
|
||||||
|
http.Error(w, "Veritabani satir hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Info("request done", "kind", "color", "model_code", modelCode, "row_count", len(list))
|
||||||
|
_ = json.NewEncoder(w).Encode(list)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Warn("request invalid", "reason", "invalid kind")
|
||||||
|
http.Error(w, "kind hammadde, item veya color olmali", http.StatusBadRequest)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/pricing/production-product-costing/has-cost-detail-exchange-rates
|
||||||
|
func GetProductionHasCostDetailExchangeRatesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
mssqlDB := db.GetDB()
|
||||||
|
if mssqlDB == nil {
|
||||||
|
http.Error(w, "MSSQL veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
|
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||||
|
logger := utils.SlogFromContext(ctx).With("handler", "production-product-costing.exchange-rates")
|
||||||
|
|
||||||
|
rawCostDate := strings.TrimSpace(r.URL.Query().Get("maliyet_tarihi"))
|
||||||
|
costDate := ""
|
||||||
|
if rawCostDate != "" {
|
||||||
|
parsedDate, err := time.Parse("2006-01-02", rawCostDate)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("request invalid", "reason", "invalid maliyet_tarihi", "maliyet_tarihi", rawCostDate)
|
||||||
|
http.Error(w, "maliyet_tarihi YYYY-MM-DD formatinda olmali", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
costDate = parsedDate.Format("2006-01-02")
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("request start", "maliyet_tarihi", costDate)
|
||||||
|
log.Printf("[ProductionHasCostDetailExchangeRates] start maliyet_tarihi=%s", costDate)
|
||||||
|
|
||||||
|
row, err := queries.GetProductionHasCostDetailExchangeRatesByDate(ctx, mssqlDB, costDate)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("query prepare error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailExchangeRates] query prepare error: %v", err)
|
||||||
|
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
item := models.ProductionHasCostDetailExchangeRates{
|
||||||
|
TRYRate: 1,
|
||||||
|
}
|
||||||
|
if err := row.Scan(
|
||||||
|
&item.RateDate,
|
||||||
|
&item.USDRate,
|
||||||
|
&item.EURRate,
|
||||||
|
&item.GBPRate,
|
||||||
|
); err != nil {
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
item.RateDate = costDate
|
||||||
|
logger.Info("request done", "rate_date", item.RateDate, "fallback", true)
|
||||||
|
_ = json.NewEncoder(w).Encode(item)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailExchangeRates] scan error: %v", err)
|
||||||
|
http.Error(w, "Veritabani satir hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("request done", "rate_date", item.RateDate, "usd_rate", item.USDRate, "eur_rate", item.EURRate, "gbp_rate", item.GBPRate)
|
||||||
|
log.Printf("[ProductionHasCostDetailExchangeRates] done maliyet_tarihi=%s rate_date=%s usd=%.4f eur=%.4f", costDate, item.RateDate, item.USDRate, item.EURRate)
|
||||||
|
_ = json.NewEncoder(w).Encode(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /api/pricing/production-product-costing/has-cost-detail-bulk-prices
|
||||||
|
func PostProductionHasCostDetailBulkPricesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
mssqlDB := db.GetDB()
|
||||||
|
if mssqlDB == nil {
|
||||||
|
http.Error(w, "MSSQL veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
|
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||||
|
logger := utils.SlogFromContext(ctx).With("handler", "production-product-costing.bulk-prices")
|
||||||
|
|
||||||
|
var req models.ProductionHasCostDetailBulkPriceRequest
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
|
logger.Warn("request invalid", "reason", "invalid request body", "err", err)
|
||||||
|
http.Error(w, "Gecersiz istek govdesi", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
costDate := strings.TrimSpace(req.MaliyetTarihi)
|
||||||
|
itemsCount := len(req.Items)
|
||||||
|
responseChan := make(chan *models.ProductionHasCostDetailBulkPriceRow, itemsCount)
|
||||||
|
|
||||||
|
logger.Info("request start",
|
||||||
|
"n_onml_no", strings.TrimSpace(req.NOnMLNo),
|
||||||
|
"urun_kodu", strings.TrimSpace(req.UrunKodu),
|
||||||
|
"maliyet_tarihi", costDate,
|
||||||
|
"item_count", itemsCount,
|
||||||
|
)
|
||||||
|
log.Printf("[ProductionHasCostDetailBulkPrices] start n_onml_no=%s urun_kodu=%s maliyet_tarihi=%s item_count=%d", strings.TrimSpace(req.NOnMLNo), strings.TrimSpace(req.UrunKodu), costDate, itemsCount)
|
||||||
|
|
||||||
|
for _, item := range req.Items {
|
||||||
|
go func(item models.ProductionHasCostDetailPriceLookupItem) {
|
||||||
|
sKodu := normalizeLookupValue(item.SKodu)
|
||||||
|
if sKodu == "" {
|
||||||
|
responseChan <- nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
colorCode := firstNonEmptyString(
|
||||||
|
normalizeLookupValue(item.ColorCode),
|
||||||
|
normalizeLookupValue(item.SRenk),
|
||||||
|
)
|
||||||
|
itemDim1Code := firstNonEmptyString(
|
||||||
|
normalizeLookupValue(item.ItemDim1Code),
|
||||||
|
)
|
||||||
|
|
||||||
|
row, err := queries.GetProductionHasCostLatestPurchasePriceForItem(
|
||||||
|
ctx,
|
||||||
|
mssqlDB,
|
||||||
|
sKodu,
|
||||||
|
colorCode,
|
||||||
|
itemDim1Code,
|
||||||
|
costDate,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("item lookup error", "s_kodu", sKodu, "color_code", colorCode, "item_dim1_code", itemDim1Code, "err", err)
|
||||||
|
responseChan <- nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var result models.ProductionHasCostDetailBulkPriceRow
|
||||||
|
if err := row.Scan(
|
||||||
|
&result.PriceType,
|
||||||
|
&result.Tarih,
|
||||||
|
&result.FaturaKodu,
|
||||||
|
&result.MasrafKodu,
|
||||||
|
&result.MasrafDetay,
|
||||||
|
&result.ColorCode,
|
||||||
|
&result.ColorDescription,
|
||||||
|
&result.ItemDim1Code,
|
||||||
|
&result.ItemDim1Description,
|
||||||
|
&result.FiyatGirilen,
|
||||||
|
&result.FiyatDoviz,
|
||||||
|
); err != nil {
|
||||||
|
logger.Warn("item scan error", "s_kodu", sKodu, "color_code", colorCode, "item_dim1_code", itemDim1Code, "err", err)
|
||||||
|
responseChan <- nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result.RowKey = strings.TrimSpace(item.RowKey)
|
||||||
|
result.NOnMLDetNo = strings.TrimSpace(item.NOnMLDetNo)
|
||||||
|
result.NHammaddeTuruNo = strings.TrimSpace(item.NHammaddeTuruNo)
|
||||||
|
result.SKodu = sKodu
|
||||||
|
|
||||||
|
if strings.TrimSpace(result.ColorCode) == "" {
|
||||||
|
result.ColorCode = colorCode
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(result.ItemDim1Code) == "" {
|
||||||
|
result.ItemDim1Code = itemDim1Code
|
||||||
|
}
|
||||||
|
|
||||||
|
responseChan <- &result
|
||||||
|
}(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
response := make([]models.ProductionHasCostDetailBulkPriceRow, 0, itemsCount)
|
||||||
|
for i := 0; i < itemsCount; i++ {
|
||||||
|
res := <-responseChan
|
||||||
|
if res != nil {
|
||||||
|
response = append(response, *res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("request done", "item_count", itemsCount, "matched_count", len(response))
|
||||||
|
log.Printf("[ProductionHasCostDetailBulkPrices] done item_count=%d matched=%d", itemsCount, len(response))
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||||
|
"items": response,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/pricing/production-product-costing/has-cost-detail-line-history
|
||||||
|
func GetProductionHasCostDetailLineHistoryHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
uretimDB := db.GetUretimDB()
|
||||||
|
mssqlDB := db.GetDB()
|
||||||
|
if uretimDB == nil && mssqlDB == nil {
|
||||||
|
http.Error(w, "Veritabani baglantilari aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
|
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||||
|
logger := utils.SlogFromContext(ctx).With("handler", "production-product-costing.line-history")
|
||||||
|
|
||||||
|
rawOnMLNo := strings.TrimSpace(r.URL.Query().Get("n_onml_no"))
|
||||||
|
currentOnMLNo, _ := strconv.Atoi(rawOnMLNo)
|
||||||
|
nHammaddeTuruNo := strings.TrimSpace(r.URL.Query().Get("n_hammadde_turu_no"))
|
||||||
|
sKodu := normalizeLookupValue(r.URL.Query().Get("s_kodu"))
|
||||||
|
colorCode := firstNonEmptyString(
|
||||||
|
normalizeLookupValue(r.URL.Query().Get("color_code")),
|
||||||
|
normalizeLookupValue(r.URL.Query().Get("s_renk")),
|
||||||
|
)
|
||||||
|
costDate := strings.TrimSpace(r.URL.Query().Get("maliyet_tarihi"))
|
||||||
|
|
||||||
|
if sKodu == "" {
|
||||||
|
logger.Warn("request invalid", "reason", "missing s_kodu")
|
||||||
|
http.Error(w, "s_kodu zorunlu", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Info("request start", "n_onml_no", currentOnMLNo, "n_hammadde_turu_no", nHammaddeTuruNo, "s_kodu", sKodu, "color_code", colorCode, "maliyet_tarihi", costDate)
|
||||||
|
log.Printf("[ProductionHasCostDetailLineHistory] start n_onml_no=%d n_hammadde_turu_no=%s s_kodu=%s color=%s maliyet_tarihi=%s", currentOnMLNo, nHammaddeTuruNo, sKodu, colorCode, costDate)
|
||||||
|
|
||||||
|
const LINE_HISTORY_ROW_LIMIT = 500
|
||||||
|
|
||||||
|
response := models.ProductionHasCostDetailLineHistoryResponse{
|
||||||
|
PurchaseRows: make([]models.ProductionHasCostDetailPurchaseHistoryRow, 0, LINE_HISTORY_ROW_LIMIT),
|
||||||
|
RecipeRows: make([]models.ProductionHasCostDetailRecipeHistoryRow, 0, LINE_HISTORY_ROW_LIMIT),
|
||||||
|
}
|
||||||
|
allowLegacyAutoFallback := true
|
||||||
|
|
||||||
|
if mssqlDB != nil {
|
||||||
|
rows, err := queries.GetProductionHasCostPurchaseHistoryByExpenseCode(
|
||||||
|
ctx,
|
||||||
|
mssqlDB,
|
||||||
|
sKodu,
|
||||||
|
costDate,
|
||||||
|
LINE_HISTORY_ROW_LIMIT,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("purchase query error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailLineHistory] purchase query error: %v", err)
|
||||||
|
} else {
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
var item models.ProductionHasCostDetailPurchaseHistoryRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&item.SourceType,
|
||||||
|
&item.PriceType,
|
||||||
|
&item.Tarih,
|
||||||
|
&item.FaturaKodu,
|
||||||
|
&item.FirmaKodu,
|
||||||
|
&item.FirmaAciklama,
|
||||||
|
&item.MasrafKodu,
|
||||||
|
&item.MasrafDetay,
|
||||||
|
&item.ColorCode,
|
||||||
|
&item.ColorDescription,
|
||||||
|
&item.ItemDim1Code,
|
||||||
|
&item.ItemDim1Description,
|
||||||
|
&item.Miktar,
|
||||||
|
&item.BIRIM,
|
||||||
|
&item.EvrakFiyat,
|
||||||
|
&item.EvrakTutar,
|
||||||
|
&item.EvrakDoviz,
|
||||||
|
); err != nil {
|
||||||
|
logger.Warn("purchase scan error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailLineHistory] purchase scan error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
response.PurchaseRows = append(response.PurchaseRows, item)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
logger.Warn("purchase rows error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailLineHistory] purchase rows error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if allowLegacyAutoFallback && mssqlDB != nil && len(response.PurchaseRows) == 0 {
|
||||||
|
similarPrefix := queries.BuildProductionHasCostSimilarCodePrefix(sKodu)
|
||||||
|
if similarPrefix != "" {
|
||||||
|
logger.Info("purchase fallback start", "s_kodu", sKodu, "similar_prefix", similarPrefix, "maliyet_tarihi", costDate)
|
||||||
|
rows, err := queries.GetProductionHasCostPurchaseHistoryByCodePrefix(
|
||||||
|
ctx,
|
||||||
|
mssqlDB,
|
||||||
|
similarPrefix,
|
||||||
|
costDate,
|
||||||
|
LINE_HISTORY_ROW_LIMIT,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("purchase fallback query error", "err", err)
|
||||||
|
log.Printf("âš ï¸ [ProductionHasCostDetailLineHistory] purchase fallback query error: %v", err)
|
||||||
|
} else {
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
var item models.ProductionHasCostDetailPurchaseHistoryRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&item.SourceType,
|
||||||
|
&item.PriceType,
|
||||||
|
&item.Tarih,
|
||||||
|
&item.FaturaKodu,
|
||||||
|
&item.FirmaKodu,
|
||||||
|
&item.FirmaAciklama,
|
||||||
|
&item.MasrafKodu,
|
||||||
|
&item.MasrafDetay,
|
||||||
|
&item.ColorCode,
|
||||||
|
&item.ColorDescription,
|
||||||
|
&item.ItemDim1Code,
|
||||||
|
&item.ItemDim1Description,
|
||||||
|
&item.Miktar,
|
||||||
|
&item.BIRIM,
|
||||||
|
&item.EvrakFiyat,
|
||||||
|
&item.EvrakTutar,
|
||||||
|
&item.EvrakDoviz,
|
||||||
|
); err != nil {
|
||||||
|
logger.Warn("purchase fallback scan error", "err", err)
|
||||||
|
log.Printf("âš ï¸ [ProductionHasCostDetailLineHistory] purchase fallback scan error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
response.PurchaseRows = append(response.PurchaseRows, item)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
logger.Warn("purchase fallback rows error", "err", err)
|
||||||
|
log.Printf("âš ï¸ [ProductionHasCostDetailLineHistory] purchase fallback rows error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if uretimDB != nil {
|
||||||
|
rows, err := queries.GetProductionHasCostRecipeHistoryByExpenseCode(
|
||||||
|
ctx,
|
||||||
|
uretimDB,
|
||||||
|
currentOnMLNo,
|
||||||
|
sKodu,
|
||||||
|
colorCode,
|
||||||
|
costDate,
|
||||||
|
LINE_HISTORY_ROW_LIMIT,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("recipe query error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailLineHistory] recipe query error: %v", err)
|
||||||
|
} else {
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
var item models.ProductionHasCostDetailRecipeHistoryRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&item.SourceType,
|
||||||
|
&item.PriceType,
|
||||||
|
&item.DteIslemTarihi,
|
||||||
|
&item.NOnMLNo,
|
||||||
|
&item.FirmaKodu,
|
||||||
|
&item.FirmaAciklama,
|
||||||
|
&item.SKodu,
|
||||||
|
&item.SAciklama,
|
||||||
|
&item.SRenk,
|
||||||
|
&item.LMiktar,
|
||||||
|
&item.SBirim,
|
||||||
|
&item.LDovizFiyati,
|
||||||
|
&item.LDovizTutari,
|
||||||
|
&item.USD,
|
||||||
|
&item.DUMMY,
|
||||||
|
); err != nil {
|
||||||
|
logger.Warn("recipe scan error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailLineHistory] recipe scan error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
response.RecipeRows = append(response.RecipeRows, item)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
logger.Warn("recipe rows error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailLineHistory] recipe rows error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if allowLegacyAutoFallback && uretimDB != nil && len(response.RecipeRows) == 0 {
|
||||||
|
similarPrefix := queries.BuildProductionHasCostSimilarCodePrefix(sKodu)
|
||||||
|
if similarPrefix != "" {
|
||||||
|
logger.Info("recipe fallback prefix start", "s_kodu", sKodu, "similar_prefix", similarPrefix, "maliyet_tarihi", costDate)
|
||||||
|
rows, err := queries.GetProductionHasCostOnMLHistoryByCodePrefix(
|
||||||
|
ctx,
|
||||||
|
uretimDB,
|
||||||
|
similarPrefix,
|
||||||
|
costDate,
|
||||||
|
LINE_HISTORY_ROW_LIMIT,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("recipe fallback prefix query error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailLineHistory] recipe fallback prefix query error: %v", err)
|
||||||
|
} else {
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
var item models.ProductionHasCostDetailRecipeHistoryRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&item.SourceType,
|
||||||
|
&item.PriceType,
|
||||||
|
&item.DteIslemTarihi,
|
||||||
|
&item.NOnMLNo,
|
||||||
|
&item.FirmaKodu,
|
||||||
|
&item.FirmaAciklama,
|
||||||
|
&item.SKodu,
|
||||||
|
&item.SAciklama,
|
||||||
|
&item.SRenk,
|
||||||
|
&item.LMiktar,
|
||||||
|
&item.SBirim,
|
||||||
|
&item.LDovizFiyati,
|
||||||
|
&item.LDovizTutari,
|
||||||
|
&item.USD,
|
||||||
|
&item.DUMMY,
|
||||||
|
); err != nil {
|
||||||
|
logger.Warn("recipe fallback prefix scan error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailLineHistory] recipe fallback prefix scan error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
response.RecipeRows = append(response.RecipeRows, item)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
logger.Warn("recipe fallback prefix rows error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailLineHistory] recipe fallback prefix rows error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if allowLegacyAutoFallback && uretimDB != nil && len(response.RecipeRows) == 0 && nHammaddeTuruNo != "" {
|
||||||
|
logger.Info("recipe fallback hammadde-turu start", "n_hammadde_turu_no", nHammaddeTuruNo, "maliyet_tarihi", costDate)
|
||||||
|
rows, err := queries.GetProductionHasCostOnMLHistoryByHammaddeTuruNo(
|
||||||
|
ctx,
|
||||||
|
uretimDB,
|
||||||
|
nHammaddeTuruNo,
|
||||||
|
costDate,
|
||||||
|
LINE_HISTORY_ROW_LIMIT,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("recipe fallback hammadde-turu query error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailLineHistory] recipe fallback hammadde-turu query error: %v", err)
|
||||||
|
} else {
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
var item models.ProductionHasCostDetailRecipeHistoryRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&item.SourceType,
|
||||||
|
&item.PriceType,
|
||||||
|
&item.DteIslemTarihi,
|
||||||
|
&item.NOnMLNo,
|
||||||
|
&item.FirmaKodu,
|
||||||
|
&item.FirmaAciklama,
|
||||||
|
&item.SKodu,
|
||||||
|
&item.SAciklama,
|
||||||
|
&item.SRenk,
|
||||||
|
&item.LMiktar,
|
||||||
|
&item.SBirim,
|
||||||
|
&item.LDovizFiyati,
|
||||||
|
&item.LDovizTutari,
|
||||||
|
&item.USD,
|
||||||
|
&item.DUMMY,
|
||||||
|
); err != nil {
|
||||||
|
logger.Warn("recipe fallback hammadde-turu scan error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailLineHistory] recipe fallback hammadde-turu scan error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
response.RecipeRows = append(response.RecipeRows, item)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
logger.Warn("recipe fallback hammadde-turu rows error", "err", err)
|
||||||
|
log.Printf("⚠️ [ProductionHasCostDetailLineHistory] recipe fallback hammadde-turu rows error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("request done", "s_kodu", sKodu, "purchase_count", len(response.PurchaseRows), "recipe_count", len(response.RecipeRows))
|
||||||
|
log.Printf("[ProductionHasCostDetailLineHistory] done s_kodu=%s purchase_rows=%d recipe_rows=%d", sKodu, len(response.PurchaseRows), len(response.RecipeRows))
|
||||||
|
_ = json.NewEncoder(w).Encode(response)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/pricing/production-product-costing/has-cost-detail-similar-history
|
||||||
|
func GetProductionHasCostDetailSimilarHistoryHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
mssqlDB := db.GetDB()
|
||||||
|
uretimDB := db.GetUretimDB()
|
||||||
|
if mssqlDB == nil || uretimDB == nil {
|
||||||
|
http.Error(w, "Veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
|
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||||
|
logger := utils.SlogFromContext(ctx).With("handler", "production-product-costing.similar-history")
|
||||||
|
|
||||||
|
q := r.URL.Query()
|
||||||
|
nHammaddeTuruNo := strings.TrimSpace(q.Get("n_hammadde_turu_no"))
|
||||||
|
sKodu := normalizeLookupValue(q.Get("s_kodu"))
|
||||||
|
costDate := strings.TrimSpace(q.Get("maliyet_tarihi"))
|
||||||
|
limit := parsePositiveIntOrDefault(q.Get("limit"), 500)
|
||||||
|
searchMode := strings.ToLower(strings.TrimSpace(q.Get("search_mode")))
|
||||||
|
if searchMode == "" || searchMode == "exact" {
|
||||||
|
searchMode = "prefix"
|
||||||
|
}
|
||||||
|
similarPrefix := queries.BuildProductionHasCostSimilarCodePrefix(sKodu)
|
||||||
|
|
||||||
|
if nHammaddeTuruNo == "" && sKodu == "" {
|
||||||
|
http.Error(w, "n_hammadde_turu_no veya s_kodu parametresi zorunludur", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("request start", "search_mode", searchMode, "n_hammadde_turu_no", nHammaddeTuruNo, "s_kodu", sKodu, "similar_prefix", similarPrefix, "maliyet_tarihi", costDate, "limit", limit)
|
||||||
|
|
||||||
|
response := models.ProductionHasCostDetailLineHistoryResponse{
|
||||||
|
PurchaseRows: make([]models.ProductionHasCostDetailPurchaseHistoryRow, 0, limit),
|
||||||
|
RecipeRows: make([]models.ProductionHasCostDetailRecipeHistoryRow, 0, limit),
|
||||||
|
}
|
||||||
|
purchaseMatchStage := searchMode
|
||||||
|
recipeMatchStage := searchMode
|
||||||
|
allowRecipeAutoFallback := false
|
||||||
|
|
||||||
|
if searchMode == "alternative" {
|
||||||
|
purchaseMatchStage = "skipped"
|
||||||
|
if nHammaddeTuruNo != "" && uretimDB != nil {
|
||||||
|
rows, err := queries.GetProductionHasCostOnMLHistoryByHammaddeTuruNo(ctx, uretimDB, nHammaddeTuruNo, costDate, limit)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("alternative onml query error", "err", err)
|
||||||
|
http.Error(w, "Sorgu calistirilirken hata olustu", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
var item models.ProductionHasCostDetailRecipeHistoryRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&item.SourceType,
|
||||||
|
&item.PriceType,
|
||||||
|
&item.DteIslemTarihi,
|
||||||
|
&item.NOnMLNo,
|
||||||
|
&item.FirmaKodu,
|
||||||
|
&item.FirmaAciklama,
|
||||||
|
&item.SKodu,
|
||||||
|
&item.SAciklama,
|
||||||
|
&item.SRenk,
|
||||||
|
&item.LMiktar,
|
||||||
|
&item.SBirim,
|
||||||
|
&item.LDovizFiyati,
|
||||||
|
&item.LDovizTutari,
|
||||||
|
&item.USD,
|
||||||
|
&item.DUMMY,
|
||||||
|
); err != nil {
|
||||||
|
logger.Warn("alternative onml scan error", "err", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
response.RecipeRows = append(response.RecipeRows, item)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
logger.Warn("alternative onml rows error", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(response.PurchaseRows) == 0 {
|
||||||
|
purchaseMatchStage = "empty"
|
||||||
|
}
|
||||||
|
if len(response.RecipeRows) == 0 {
|
||||||
|
recipeMatchStage = "empty"
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("request done",
|
||||||
|
"search_mode", searchMode,
|
||||||
|
"purchase_match_stage", purchaseMatchStage,
|
||||||
|
"recipe_match_stage", recipeMatchStage,
|
||||||
|
"similar_prefix", similarPrefix,
|
||||||
|
"purchase_count", len(response.PurchaseRows),
|
||||||
|
"recipe_count", len(response.RecipeRows),
|
||||||
|
)
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||||
|
"purchaseRows": response.PurchaseRows,
|
||||||
|
"recipeRows": response.RecipeRows,
|
||||||
|
"purchase_match_stage": purchaseMatchStage,
|
||||||
|
"recipe_match_stage": recipeMatchStage,
|
||||||
|
"similar_code_prefix": similarPrefix,
|
||||||
|
"search_mode": searchMode,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if similarPrefix != "" {
|
||||||
|
if mssqlDB != nil {
|
||||||
|
rows, err := queries.GetProductionHasCostPurchaseHistoryByCodePrefix(ctx, mssqlDB, similarPrefix, costDate, limit)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("prefix purchase query error", "err", err)
|
||||||
|
} else {
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
var item models.ProductionHasCostDetailPurchaseHistoryRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&item.SourceType,
|
||||||
|
&item.PriceType,
|
||||||
|
&item.Tarih,
|
||||||
|
&item.FaturaKodu,
|
||||||
|
&item.FirmaKodu,
|
||||||
|
&item.FirmaAciklama,
|
||||||
|
&item.MasrafKodu,
|
||||||
|
&item.MasrafDetay,
|
||||||
|
&item.ColorCode,
|
||||||
|
&item.ColorDescription,
|
||||||
|
&item.ItemDim1Code,
|
||||||
|
&item.ItemDim1Description,
|
||||||
|
&item.Miktar,
|
||||||
|
&item.BIRIM,
|
||||||
|
&item.EvrakFiyat,
|
||||||
|
&item.EvrakTutar,
|
||||||
|
&item.EvrakDoviz,
|
||||||
|
); err != nil {
|
||||||
|
logger.Warn("prefix purchase scan error", "err", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
response.PurchaseRows = append(response.PurchaseRows, item)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
logger.Warn("prefix purchase rows error", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if uretimDB != nil {
|
||||||
|
rows, err := queries.GetProductionHasCostOnMLHistoryByCodePrefix(ctx, uretimDB, similarPrefix, costDate, limit)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("prefix onml query error", "err", err)
|
||||||
|
} else {
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
var item models.ProductionHasCostDetailRecipeHistoryRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&item.SourceType,
|
||||||
|
&item.PriceType,
|
||||||
|
&item.DteIslemTarihi,
|
||||||
|
&item.NOnMLNo,
|
||||||
|
&item.FirmaKodu,
|
||||||
|
&item.FirmaAciklama,
|
||||||
|
&item.SKodu,
|
||||||
|
&item.SAciklama,
|
||||||
|
&item.SRenk,
|
||||||
|
&item.LMiktar,
|
||||||
|
&item.SBirim,
|
||||||
|
&item.LDovizFiyati,
|
||||||
|
&item.LDovizTutari,
|
||||||
|
&item.USD,
|
||||||
|
&item.DUMMY,
|
||||||
|
); err != nil {
|
||||||
|
logger.Warn("prefix onml scan error", "err", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
response.RecipeRows = append(response.RecipeRows, item)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
logger.Warn("prefix onml rows error", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if allowRecipeAutoFallback && len(response.RecipeRows) == 0 && nHammaddeTuruNo != "" && uretimDB != nil {
|
||||||
|
recipeMatchStage = "hammadde-turu-fallback"
|
||||||
|
rows, err := queries.GetProductionHasCostOnMLHistoryByHammaddeTuruNo(ctx, uretimDB, nHammaddeTuruNo, costDate, limit)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("fallback onml query error", "err", err)
|
||||||
|
http.Error(w, "Sorgu calistirilirken hata olustu", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
for rows.Next() {
|
||||||
|
var item models.ProductionHasCostDetailRecipeHistoryRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&item.SourceType,
|
||||||
|
&item.PriceType,
|
||||||
|
&item.DteIslemTarihi,
|
||||||
|
&item.NOnMLNo,
|
||||||
|
&item.FirmaKodu,
|
||||||
|
&item.FirmaAciklama,
|
||||||
|
&item.SKodu,
|
||||||
|
&item.SAciklama,
|
||||||
|
&item.SRenk,
|
||||||
|
&item.LMiktar,
|
||||||
|
&item.SBirim,
|
||||||
|
&item.LDovizFiyati,
|
||||||
|
&item.LDovizTutari,
|
||||||
|
&item.USD,
|
||||||
|
&item.DUMMY,
|
||||||
|
); err != nil {
|
||||||
|
logger.Warn("fallback onml scan error", "err", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
response.RecipeRows = append(response.RecipeRows, item)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
logger.Warn("fallback onml rows error", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(response.PurchaseRows) == 0 {
|
||||||
|
purchaseMatchStage = "empty"
|
||||||
|
}
|
||||||
|
if len(response.RecipeRows) == 0 {
|
||||||
|
recipeMatchStage = "empty"
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("request done",
|
||||||
|
"search_mode", searchMode,
|
||||||
|
"purchase_match_stage", purchaseMatchStage,
|
||||||
|
"recipe_match_stage", recipeMatchStage,
|
||||||
|
"similar_prefix", similarPrefix,
|
||||||
|
"purchase_count", len(response.PurchaseRows),
|
||||||
|
"recipe_count", len(response.RecipeRows),
|
||||||
|
)
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||||
|
"purchaseRows": response.PurchaseRows,
|
||||||
|
"recipeRows": response.RecipeRows,
|
||||||
|
"purchase_match_stage": purchaseMatchStage,
|
||||||
|
"recipe_match_stage": recipeMatchStage,
|
||||||
|
"similar_code_prefix": similarPrefix,
|
||||||
|
"search_mode": searchMode,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePositiveIntOrDefault(raw string, fallback int) int {
|
||||||
|
v, err := strconv.Atoi(strings.TrimSpace(raw))
|
||||||
|
if err != nil || v < 0 {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeLookupValue(raw string) string {
|
||||||
|
return strings.ToUpper(strings.TrimSpace(raw))
|
||||||
|
}
|
||||||
|
|
||||||
|
func firstNonEmptyString(values ...string) string {
|
||||||
|
for _, value := range values {
|
||||||
|
value = strings.TrimSpace(value)
|
||||||
|
if value != "" {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// MT BOLUM MAPPING (URETIM DB)
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
// GET /api/pricing/production-product-costing/options/urun-ana-grup
|
||||||
|
func GetProductionProductCostingUrunAnaGrupOptionsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
mssqlDB := db.GetDB()
|
||||||
|
if mssqlDB == nil {
|
||||||
|
http.Error(w, "MSSQL veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
|
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||||
|
search := strings.TrimSpace(r.URL.Query().Get("search"))
|
||||||
|
limit := parsePositiveIntOrDefault(r.URL.Query().Get("limit"), 50)
|
||||||
|
if limit > 500 {
|
||||||
|
limit = 500
|
||||||
|
}
|
||||||
|
|
||||||
|
logger := utils.SlogFromContext(ctx).With(
|
||||||
|
"handler", "production-product-costing.options.urun-ana-grup",
|
||||||
|
"search", search,
|
||||||
|
"limit", limit,
|
||||||
|
)
|
||||||
|
logger.Info("request start")
|
||||||
|
|
||||||
|
rows, err := queries.GetProductionProductCostingAnaGrupOptions(ctx, mssqlDB, search, limit)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("query error", "err", err)
|
||||||
|
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
out := make([]models.ProductionProductCostingLookupOption, 0, limit)
|
||||||
|
for rows.Next() {
|
||||||
|
var v string
|
||||||
|
if err := rows.Scan(&v); err != nil {
|
||||||
|
logger.Warn("scan error", "err", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
v = strings.TrimSpace(v)
|
||||||
|
if v == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, models.ProductionProductCostingLookupOption{Value: v, Label: v})
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
logger.Error("rows error", "err", err)
|
||||||
|
http.Error(w, "Veritabani satir hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Info("request done", "row_count", len(out))
|
||||||
|
_ = json.NewEncoder(w).Encode(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/pricing/production-product-costing/options/urun-alt-grup
|
||||||
|
func GetProductionProductCostingUrunAltGrupOptionsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
mssqlDB := db.GetDB()
|
||||||
|
if mssqlDB == nil {
|
||||||
|
http.Error(w, "MSSQL veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
|
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||||
|
urunAnaGrubu := strings.TrimSpace(r.URL.Query().Get("urun_ana_grubu"))
|
||||||
|
search := strings.TrimSpace(r.URL.Query().Get("search"))
|
||||||
|
limit := parsePositiveIntOrDefault(r.URL.Query().Get("limit"), 50)
|
||||||
|
if limit > 500 {
|
||||||
|
limit = 500
|
||||||
|
}
|
||||||
|
|
||||||
|
logger := utils.SlogFromContext(ctx).With(
|
||||||
|
"handler", "production-product-costing.options.urun-alt-grup",
|
||||||
|
"urun_ana_grubu", urunAnaGrubu,
|
||||||
|
"search", search,
|
||||||
|
"limit", limit,
|
||||||
|
)
|
||||||
|
logger.Info("request start")
|
||||||
|
|
||||||
|
rows, err := queries.GetProductionProductCostingAltGrupOptions(ctx, mssqlDB, urunAnaGrubu, search, limit)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("query error", "err", err)
|
||||||
|
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
out := make([]models.ProductionProductCostingLookupOption, 0, limit)
|
||||||
|
for rows.Next() {
|
||||||
|
var v string
|
||||||
|
if err := rows.Scan(&v); err != nil {
|
||||||
|
logger.Warn("scan error", "err", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
v = strings.TrimSpace(v)
|
||||||
|
if v == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, models.ProductionProductCostingLookupOption{Value: v, Label: v})
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
logger.Error("rows error", "err", err)
|
||||||
|
http.Error(w, "Veritabani satir hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Info("request done", "row_count", len(out))
|
||||||
|
_ = json.NewEncoder(w).Encode(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/pricing/production-product-costing/options/urun-ana-alt-combos
|
||||||
|
func GetProductionProductCostingUrunAnaAltCombosHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
mssqlDB := db.GetDB()
|
||||||
|
if mssqlDB == nil {
|
||||||
|
http.Error(w, "MSSQL veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
|
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||||
|
search := strings.TrimSpace(r.URL.Query().Get("search"))
|
||||||
|
limit := parsePositiveIntOrDefault(r.URL.Query().Get("limit"), 2000)
|
||||||
|
if limit > 5000 {
|
||||||
|
limit = 5000
|
||||||
|
}
|
||||||
|
|
||||||
|
logger := utils.SlogFromContext(ctx).With(
|
||||||
|
"handler", "production-product-costing.options.urun-ana-alt-combos",
|
||||||
|
"search", search,
|
||||||
|
"limit", limit,
|
||||||
|
)
|
||||||
|
logger.Info("request start")
|
||||||
|
|
||||||
|
rows, err := queries.GetProductionProductCostingAnaAltComboRows(ctx, mssqlDB, search, limit)
|
||||||
|
if err != nil {
|
||||||
|
// Keep the response generic, but log the underlying SQL driver error for diagnostics.
|
||||||
|
logger.Error("query error", "err", err, "search", search, "limit", limit, "trace_id", traceID)
|
||||||
|
log.Printf("❌ [ProductionProductCostingAnaAltCombos] query error trace_id=%s search=%q limit=%d err=%v", traceID, search, limit, err)
|
||||||
|
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
out := make([]models.ProductionProductCostingAnaAltComboRow, 0, 1024)
|
||||||
|
for rows.Next() {
|
||||||
|
var item models.ProductionProductCostingAnaAltComboRow
|
||||||
|
if err := rows.Scan(&item.UrunIlkGrubu, &item.UrunAnaGrubu, &item.UrunAltGrubu); err != nil {
|
||||||
|
logger.Warn("scan error", "err", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
item.UrunIlkGrubu = strings.TrimSpace(item.UrunIlkGrubu)
|
||||||
|
item.UrunAnaGrubu = strings.TrimSpace(item.UrunAnaGrubu)
|
||||||
|
item.UrunAltGrubu = strings.TrimSpace(item.UrunAltGrubu)
|
||||||
|
if item.UrunIlkGrubu == "" || item.UrunAnaGrubu == "" || item.UrunAltGrubu == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, item)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
logger.Error("rows error", "err", err, "trace_id", traceID)
|
||||||
|
log.Printf("⚠️ [ProductionProductCostingAnaAltCombos] rows error trace_id=%s err=%v", traceID, err)
|
||||||
|
http.Error(w, "Veritabani satir hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("request done", "row_count", len(out))
|
||||||
|
_ = json.NewEncoder(w).Encode(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/pricing/production-product-costing/options/mtbolum
|
||||||
|
func GetProductionProductCostingMTBolumOptionsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
uretimDB := db.GetUretimDB()
|
||||||
|
if uretimDB == nil {
|
||||||
|
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
|
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||||
|
search := strings.TrimSpace(r.URL.Query().Get("search"))
|
||||||
|
limit := parsePositiveIntOrDefault(r.URL.Query().Get("limit"), 50)
|
||||||
|
if limit > 500 {
|
||||||
|
limit = 500
|
||||||
|
}
|
||||||
|
|
||||||
|
logger := utils.SlogFromContext(ctx).With(
|
||||||
|
"handler", "production-product-costing.options.mtbolum",
|
||||||
|
"search", search,
|
||||||
|
"limit", limit,
|
||||||
|
)
|
||||||
|
logger.Info("request start")
|
||||||
|
|
||||||
|
rows, err := queries.GetProductionProductCostingMTBolumOptions(ctx, uretimDB, search, limit)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("query error", "err", err)
|
||||||
|
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
out := make([]models.ProductionProductCostingLookupOption, 0, limit)
|
||||||
|
for rows.Next() {
|
||||||
|
var id int
|
||||||
|
var name string
|
||||||
|
if err := rows.Scan(&id, &name); err != nil {
|
||||||
|
logger.Warn("scan error", "err", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
label := strings.TrimSpace(strconv.Itoa(id) + " - " + strings.TrimSpace(name))
|
||||||
|
out = append(out, models.ProductionProductCostingLookupOption{
|
||||||
|
Value: strconv.Itoa(id),
|
||||||
|
Label: label,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
logger.Error("rows error", "err", err)
|
||||||
|
http.Error(w, "Veritabani satir hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Info("request done", "row_count", len(out))
|
||||||
|
_ = json.NewEncoder(w).Encode(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET /api/pricing/production-product-costing/maliyet-parca-eslestirme
|
||||||
|
func GetProductionProductCostingParcaMappingsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
uretimDB := db.GetUretimDB()
|
||||||
|
if uretimDB == nil {
|
||||||
|
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
|
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||||
|
urunIlkGrubu := strings.TrimSpace(r.URL.Query().Get("urun_ilk_grubu"))
|
||||||
|
urunAnaGrubu := strings.TrimSpace(r.URL.Query().Get("urun_ana_grubu"))
|
||||||
|
urunAltGrubu := strings.TrimSpace(r.URL.Query().Get("urun_alt_grubu"))
|
||||||
|
nUrtMTBolumID := parsePositiveIntOrDefault(r.URL.Query().Get("n_urt_mt_bolum_id"), 0)
|
||||||
|
rawOnlyActive := strings.TrimSpace(r.URL.Query().Get("only_active"))
|
||||||
|
var onlyActive *bool = nil
|
||||||
|
if rawOnlyActive != "" {
|
||||||
|
v := rawOnlyActive == "1" || strings.EqualFold(rawOnlyActive, "true")
|
||||||
|
onlyActive = &v
|
||||||
|
}
|
||||||
|
|
||||||
|
logger := utils.SlogFromContext(ctx).With(
|
||||||
|
"handler", "production-product-costing.maliyet-parca-eslestirme.list",
|
||||||
|
"urun_ilk_grubu", urunIlkGrubu,
|
||||||
|
"urun_ana_grubu", urunAnaGrubu,
|
||||||
|
"urun_alt_grubu", urunAltGrubu,
|
||||||
|
"n_urt_mt_bolum_id", nUrtMTBolumID,
|
||||||
|
"only_active", rawOnlyActive,
|
||||||
|
)
|
||||||
|
logger.Info("request start")
|
||||||
|
|
||||||
|
rows, err := queries.ListProductionProductCostingParcaMappings(ctx, uretimDB, urunIlkGrubu, urunAnaGrubu, urunAltGrubu, nUrtMTBolumID, onlyActive)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("query error", "err", err)
|
||||||
|
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
out := make([]models.ProductionProductCostingParcaMappingRow, 0, 200)
|
||||||
|
for rows.Next() {
|
||||||
|
var row models.ProductionProductCostingParcaMappingRow
|
||||||
|
var bAktif sql.NullBool
|
||||||
|
var hammaddeCsv sql.NullString
|
||||||
|
if err := rows.Scan(
|
||||||
|
&row.ID,
|
||||||
|
&row.UrunIlkGrubu,
|
||||||
|
&row.UrunAnaGrubu,
|
||||||
|
&row.UrunAltGrubu,
|
||||||
|
&row.NUrtMTBolumID,
|
||||||
|
&row.ParcaBolumAdi,
|
||||||
|
&hammaddeCsv,
|
||||||
|
&bAktif,
|
||||||
|
&row.DteIslem,
|
||||||
|
&row.SKullaniciAdi,
|
||||||
|
); err != nil {
|
||||||
|
logger.Warn("scan error", "err", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
row.BAktif = bAktif.Valid && bAktif.Bool
|
||||||
|
row.NHammaddeTurleri = make([]string, 0, 8)
|
||||||
|
if hammaddeCsv.Valid {
|
||||||
|
for _, part := range strings.Split(hammaddeCsv.String, ",") {
|
||||||
|
part = strings.TrimSpace(part)
|
||||||
|
if part != "" {
|
||||||
|
row.NHammaddeTurleri = append(row.NHammaddeTurleri, part)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out = append(out, row)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
logger.Error("rows error", "err", err)
|
||||||
|
http.Error(w, "Veritabani satir hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Info("request done", "row_count", len(out))
|
||||||
|
_ = json.NewEncoder(w).Encode(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /api/pricing/production-product-costing/maliyet-parca-eslestirme/upsert
|
||||||
|
func PostProductionProductCostingParcaMappingUpsertHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
uretimDB := db.GetUretimDB()
|
||||||
|
if uretimDB == nil {
|
||||||
|
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
claims, _ := auth.GetClaimsFromContext(r.Context())
|
||||||
|
user := ""
|
||||||
|
if claims != nil {
|
||||||
|
user = strings.TrimSpace(claims.Username)
|
||||||
|
}
|
||||||
|
|
||||||
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
|
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||||
|
logger := utils.SlogFromContext(ctx).With("handler", "production-product-costing.maliyet-parca-eslestirme.upsert")
|
||||||
|
logger.Info("request start")
|
||||||
|
|
||||||
|
var req models.ProductionProductCostingParcaMappingUpsertRequest
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
|
logger.Warn("invalid json", "err", err)
|
||||||
|
http.Error(w, "Gecersiz JSON", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
req.UrunAnaGrubu = strings.TrimSpace(req.UrunAnaGrubu)
|
||||||
|
req.UrunAltGrubu = strings.TrimSpace(req.UrunAltGrubu)
|
||||||
|
req.UrunIlkGrubu = strings.TrimSpace(req.UrunIlkGrubu)
|
||||||
|
if req.UrunIlkGrubu == "" || req.UrunAnaGrubu == "" || req.UrunAltGrubu == "" || req.NUrtMTBolumID <= 0 {
|
||||||
|
http.Error(w, "urunIlkGrubu, urunAnaGrubu, urunAltGrubu ve nUrtMTBolumID zorunlu", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := queries.UpsertProductionProductCostingParcaMapping(ctx, uretimDB, req.UrunIlkGrubu, req.UrunAnaGrubu, req.UrunAltGrubu, req.NUrtMTBolumID, req.NHammaddeTurleri, req.BAktif, user)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("exec error", "err", err)
|
||||||
|
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("request done", "id", id, "user", user, "bAktif", req.BAktif)
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"ok": true, "id": id})
|
||||||
|
}
|
||||||
|
|
||||||
|
type productionProductCostingSetActiveRequest struct {
|
||||||
|
ID int `json:"id"`
|
||||||
|
BAktif bool `json:"bAktif"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// POST /api/pricing/production-product-costing/maliyet-parca-eslestirme/set-active
|
||||||
|
func PostProductionProductCostingParcaMappingSetActiveHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
uretimDB := db.GetUretimDB()
|
||||||
|
if uretimDB == nil {
|
||||||
|
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
claims, _ := auth.GetClaimsFromContext(r.Context())
|
||||||
|
user := ""
|
||||||
|
if claims != nil {
|
||||||
|
user = strings.TrimSpace(claims.Username)
|
||||||
|
}
|
||||||
|
|
||||||
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
|
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||||
|
logger := utils.SlogFromContext(ctx).With("handler", "production-product-costing.maliyet-parca-eslestirme.set-active")
|
||||||
|
logger.Info("request start")
|
||||||
|
|
||||||
|
var req productionProductCostingSetActiveRequest
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
|
logger.Warn("invalid json", "err", err)
|
||||||
|
http.Error(w, "Gecersiz JSON", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if req.ID <= 0 {
|
||||||
|
http.Error(w, "id zorunlu", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := queries.SetProductionProductCostingParcaMappingActive(ctx, uretimDB, req.ID, req.BAktif, user); err != nil {
|
||||||
|
logger.Error("exec error", "err", err)
|
||||||
|
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("request done", "id", req.ID, "bAktif", req.BAktif, "user", user)
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"ok": true})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE /api/pricing/production-product-costing/maliyet-parca-eslestirme?id=123
|
||||||
|
func DeleteProductionProductCostingParcaMappingHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
uretimDB := db.GetUretimDB()
|
||||||
|
if uretimDB == nil {
|
||||||
|
http.Error(w, "URETIM veritabani baglantisi aktif degil", http.StatusServiceUnavailable)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
traceID := utils.TraceIDFromRequest(r)
|
||||||
|
ctx := utils.ContextWithTraceID(r.Context(), traceID)
|
||||||
|
logger := utils.SlogFromContext(ctx).With("handler", "production-product-costing.maliyet-parca-eslestirme.delete")
|
||||||
|
logger.Info("request start")
|
||||||
|
|
||||||
|
id := parsePositiveIntOrDefault(r.URL.Query().Get("id"), 0)
|
||||||
|
if id <= 0 {
|
||||||
|
http.Error(w, "id zorunlu", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := queries.DeleteProductionProductCostingParcaMapping(ctx, uretimDB, id); err != nil {
|
||||||
|
logger.Error("exec error", "err", err)
|
||||||
|
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Info("request done", "id", id)
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{"ok": true})
|
||||||
|
}
|
||||||
1
svc/tmp_backend.err.log
Normal file
1
svc/tmp_backend.err.log
Normal file
@@ -0,0 +1 @@
|
|||||||
|
exit status 1
|
||||||
157
svc/tmp_backend.out.log
Normal file
157
svc/tmp_backend.out.log
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
time=2026-05-03T21:39:36.490+03:00 level=INFO msg="backend start" app=bssapp-backend scope=main
|
||||||
|
time=2026-05-03T21:39:36.508+03:00 level=INFO msg="🔥🔥🔥 BSSAPP BACKEND STARTED — LOGIN ROUTE SHOULD EXIST 🔥🔥🔥" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:36.509+03:00 level=INFO msg="🔐 JWT_SECRET yüklendi" app=bssapp-backend
|
||||||
|
MSSQL baglantisi basarili (connection timeout=120s, dial timeout=120s)
|
||||||
|
URETIM MSSQL baglantisi basarili (connection timeout=120s, dial timeout=120s)
|
||||||
|
time=2026-05-03T21:39:36.926+03:00 level=INFO msg="PostgreSQL bağlantısı başarılı" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:37.222+03:00 level=INFO msg="✅ Admin dept permissions seeded" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:37.223+03:00 level=INFO msg="🟢 auditlog Init called, buffer: 1000" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:37.224+03:00 level=INFO msg="🕵️ AuditLog sistemi başlatıldı (buffer=1000)" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:37.224+03:00 level=INFO msg="🟢 auditlog worker STARTED" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:37.306+03:00 level=INFO msg="[TranslationPerf] index_ready sql=\"CREATE EXTENSION IF NOT EXISTS pg_trgm\"" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:37.386+03:00 level=INFO msg="[TranslationPerf] index_ready sql=\"CREATE INDEX IF NOT EXISTS idx_mk_translator_t_key_lang ON mk_translator (t_key, lang_code)\"" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:37.471+03:00 level=INFO msg="[TranslationPerf] index_ready sql=\"CREATE INDEX IF NOT EXISTS idx_mk_translator_status_lang_updated ON mk_translator (status, lang_code...\"" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:37.553+03:00 level=INFO msg="[TranslationPerf] index_ready sql=\"CREATE INDEX IF NOT EXISTS idx_mk_translator_manual_status ON mk_translator (is_manual, status)\"" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:37.635+03:00 level=INFO msg="[TranslationPerf] index_ready sql=\"CREATE INDEX IF NOT EXISTS idx_mk_translator_source_type_expr ON mk_translator ((COALESCE(NULLIF(pro...\"" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:37.717+03:00 level=INFO msg="[TranslationPerf] index_ready sql=\"CREATE INDEX IF NOT EXISTS idx_mk_translator_source_text_trgm ON mk_translator USING gin (source_tex...\"" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:37.799+03:00 level=INFO msg="[TranslationPerf] index_ready sql=\"CREATE INDEX IF NOT EXISTS idx_mk_translator_translated_text_trgm ON mk_translator USING gin (transl...\"" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:37.799+03:00 level=INFO msg="✉️ Graph Mailer hazır (App-only token) | from=baggiss@baggi.com.tr" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:37.799+03:00 level=INFO msg="✉️ Graph Mailer hazır" app=bssapp-backend
|
||||||
|
📋 [DEBUG] İlk 10 kullanıcı:
|
||||||
|
- 1 : ctengiz
|
||||||
|
- 2 : ali.kale
|
||||||
|
- 5 : mehmet.keçeci
|
||||||
|
- 6 : mert.keçeci
|
||||||
|
- 7 : samet.keçeci
|
||||||
|
- 9 : orhan.caliskan
|
||||||
|
- 10 : nilgun.sara
|
||||||
|
- 14 : rustem.kurbanov
|
||||||
|
- 15 : caner.akyol
|
||||||
|
- 16 : kemal.matyakupov
|
||||||
|
time=2026-05-03T21:39:38.702+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/auth/login [auth:login]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:39.525+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/auth/refresh [auth:refresh]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:40.343+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/password/forgot [auth:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:41.180+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/password/reset/validate/{token} [auth:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:41.998+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/password/reset [auth:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:42.815+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/password/change [auth:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:43.636+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/activity-logs [system:read]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:44.451+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/test-mail [system:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:45.268+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/system/market-mail-mappings/lookups [system:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:46.088+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/system/market-mail-mappings [system:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:46.905+03:00 level=INFO msg="✅ Route+Perm registered → PUT /api/system/market-mail-mappings/{marketId} [system:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:47.722+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/language/translations [language:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:48.540+03:00 level=INFO msg="✅ Route+Perm registered → PUT /api/language/translations/{id} [language:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:49.366+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/language/translations/upsert-missing [language:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:50.191+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/language/translations/sync-sources [language:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:51.011+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/language/translations/translate-selected [language:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:51.827+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/language/translations/bulk-approve [language:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:52.647+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/language/translations/bulk-update [language:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:53.473+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/roles/{id}/permissions [system:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:54.310+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/roles/{id}/permissions [system:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:55.165+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/users/{id}/permissions [system:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:56.030+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/users/{id}/permissions [system:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:56.877+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/permissions/routes [system:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:57.718+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/permissions/effective [system:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:58.552+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/permissions/matrix [system:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:39:59.385+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/role-dept-permissions/list [system:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:00.208+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/roles/{roleId}/departments/{deptCode}/permissions [system:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:01.043+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/roles/{roleId}/departments/{deptCode}/permissions [system:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:01.861+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/users/list [user:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:02.685+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/users [user:insert]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:03.509+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/users/{id} [user:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:04.357+03:00 level=INFO msg="✅ Route+Perm registered → PUT /api/users/{id} [user:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:05.218+03:00 level=INFO msg="✅ Route+Perm registered → DELETE /api/users/{id} [user:delete]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:06.102+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/users/{id}/admin-reset-password [user:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:06.946+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/users/{id}/send-password-mail [user:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:07.760+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/users/create [user:insert]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:08.577+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/lookups/modules [user:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:09.402+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/lookups/roles [user:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:10.216+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/lookups/departments [user:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:11.051+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/lookups/nebim-users [user:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:11.932+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/lookups/piyasalar [user:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:12.770+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/lookups/users-perm [user:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:13.610+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/lookups/roles-perm [user:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:14.445+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/lookups/departments-perm [user:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:15.268+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/accounts [customer:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:16.090+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/customer-list [customer:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:16.921+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/today-currency [finance:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:17.735+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/export-pdf [finance:export]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:18.573+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/exportstamentheaderreport-pdf [finance:export]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:19.416+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/finance/customer-balances [finance:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:20.262+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/finance/customer-balances/export-pdf [finance:export]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:21.096+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/finance/customer-balances/export-excel [finance:export]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:21.915+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/finance/account-aging-statement [finance:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:22.770+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/finance/account-aging-statement/export-pdf [finance:export]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:23.607+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/finance/account-aging-statement/export-screen-pdf [finance:export]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:24.427+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/finance/account-aging-statement/export-excel [finance:export]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:25.254+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/finance/aged-customer-balance-list [finance:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:26.082+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/statements [finance:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:26.915+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/statements/{id}/details [finance:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:27.733+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/order/create [order:insert]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:28.560+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/order/update [order:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:29.446+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/order/{id}/bulk-due-date [order:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:30.278+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/order/get/{id} [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:31.103+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/orders/list [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:31.929+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/orders/production-list [order:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:32.753+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/orders/production-items/cditem-lookups [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:33.571+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/orders/production-items/{id} [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:34.385+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/orders/production-items/{id}/insert-missing [order:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:35.206+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/orders/production-items/{id}/validate [order:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:36.027+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/orders/production-items/{id}/apply [order:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:36.841+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/orders/close-ready [order:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:37.670+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/orders/bulk-close [order:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:38.506+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/orders/export [order:export]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:39.341+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/order/check/{id} [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:40.201+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/order/validate [order:insert]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:41.036+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/order/pdf/{id} [order:export]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:41.857+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/order/send-market-mail [order:read]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:42.673+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/order-inventory [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:43.498+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/orderpricelistb2b [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:44.313+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/min-price [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:45.140+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/products [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:45.993+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-detail [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:46.836+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-cditem [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:47.696+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-colors [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:48.557+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-newcolors [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:49.393+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-colorsize [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:50.212+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-secondcolor [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:51.042+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-newsecondcolor [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:51.871+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-attributes [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:52.704+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-item-attributes [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:53.518+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-stock-query [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:54.344+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-stock-attribute-options [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:55.177+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-stock-query-by-attributes [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:56.013+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-images [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:56.845+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-images/{id}/content [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:57.677+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/product-size-match/rules [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:58.502+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/products [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:40:59.343+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/no-cost-products [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:00.155+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/has-cost-products [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:00.974+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/has-cost-history [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:01.791+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/has-cost-detail-groups [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:02.612+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/has-cost-detail-header [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:03.482+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/production-types [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:04.331+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/detail-editor-options [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:05.183+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/has-cost-detail-exchange-rates [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:06.013+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/has-cost-detail-line-history [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:06.839+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/has-cost-detail-similar-history [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:07.662+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/pricing/production-product-costing/has-cost-detail-bulk-prices [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:08.497+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/options/urun-ana-grup [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:09.318+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/options/urun-alt-grup [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:10.135+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/options/urun-ana-alt-combos [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:10.952+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/options/mtbolum [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:11.795+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/pricing/production-product-costing/maliyet-parca-eslestirme [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:12.633+03:00 level=INFO msg="✅ Route+Perm registered → DELETE /api/pricing/production-product-costing/maliyet-parca-eslestirme [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:13.473+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/pricing/production-product-costing/maliyet-parca-eslestirme/upsert [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:14.327+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/pricing/production-product-costing/maliyet-parca-eslestirme/set-active [order:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:15.176+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/roles [user:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:16.018+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/departments [user:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:16.855+03:00 level=INFO msg="✅ Route+Perm registered → GET /api/piyasalar [user:view]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:17.674+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/roles/{id}/departments [user:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:18.487+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/roles/{id}/piyasalar [user:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:19.308+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/users/{id}/roles [user:update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:20.120+03:00 level=INFO msg="✅ Route+Perm registered → POST /api/admin/users/{id}/piyasa-sync [admin:user.update]" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:20.120+03:00 level=INFO msg="🌍 CORS Allowed Origin: http://ss.baggi.com.tr/app" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:20.121+03:00 level=INFO msg="🚀 Server running at: 0.0.0.0:8080" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:20.121+03:00 level=INFO msg="🕓 Translation sync next run at 2026-05-04T04:00:00+03:00 (in 6h18m40s)" app=bssapp-backend
|
||||||
|
time=2026-05-03T21:41:20.122+03:00 level=INFO msg="listen tcp 0.0.0.0:8080: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted." app=bssapp-backend
|
||||||
78
svc/utils/slog.go
Normal file
78
svc/utils/slog.go
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type traceIDContextKey string
|
||||||
|
|
||||||
|
const traceIDKey traceIDContextKey = "trace_id"
|
||||||
|
|
||||||
|
func InitSlog() {
|
||||||
|
level := new(slog.LevelVar)
|
||||||
|
switch strings.ToLower(strings.TrimSpace(os.Getenv("SLOG_LEVEL"))) {
|
||||||
|
case "debug":
|
||||||
|
level.Set(slog.LevelDebug)
|
||||||
|
case "warn":
|
||||||
|
level.Set(slog.LevelWarn)
|
||||||
|
case "error":
|
||||||
|
level.Set(slog.LevelError)
|
||||||
|
default:
|
||||||
|
level.Set(slog.LevelInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
|
||||||
|
Level: level,
|
||||||
|
AddSource: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
slog.SetDefault(slog.New(handler).With("app", "bssapp-backend"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TraceIDFromRequest(r *http.Request) string {
|
||||||
|
if r == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
candidates := []string{
|
||||||
|
strings.TrimSpace(r.Header.Get("X-Trace-ID")),
|
||||||
|
strings.TrimSpace(r.URL.Query().Get("trace_id")),
|
||||||
|
strings.TrimSpace(r.Header.Get("X-Request-ID")),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, candidate := range candidates {
|
||||||
|
if candidate != "" {
|
||||||
|
return candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func ContextWithTraceID(ctx context.Context, traceID string) context.Context {
|
||||||
|
traceID = strings.TrimSpace(traceID)
|
||||||
|
if ctx == nil || traceID == "" {
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
return context.WithValue(ctx, traceIDKey, traceID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TraceIDFromContext(ctx context.Context) string {
|
||||||
|
if ctx == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
value, _ := ctx.Value(traceIDKey).(string)
|
||||||
|
return strings.TrimSpace(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SlogFromContext(ctx context.Context) *slog.Logger {
|
||||||
|
traceID := TraceIDFromContext(ctx)
|
||||||
|
if traceID == "" {
|
||||||
|
return slog.Default()
|
||||||
|
}
|
||||||
|
return slog.Default().With("trace_id", traceID)
|
||||||
|
}
|
||||||
75
ui/.quasar/prod-spa/app.js
Normal file
75
ui/.quasar/prod-spa/app.js
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/**
|
||||||
|
* THIS FILE IS GENERATED AUTOMATICALLY.
|
||||||
|
* DO NOT EDIT.
|
||||||
|
*
|
||||||
|
* You are probably looking on adding startup/initialization code.
|
||||||
|
* Use "quasar new boot <name>" and add it there.
|
||||||
|
* One boot file per concern. Then reference the file(s) in quasar.config file > boot:
|
||||||
|
* boot: ['file', ...] // do not add ".js" extension to it.
|
||||||
|
*
|
||||||
|
* Boot files are your "main.js"
|
||||||
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import { Quasar } from 'quasar'
|
||||||
|
import { markRaw } from 'vue'
|
||||||
|
import RootComponent from 'app/src/App.vue'
|
||||||
|
|
||||||
|
import createStore from 'app/src/stores/index'
|
||||||
|
import createRouter from 'app/src/router/index'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default async function (createAppFn, quasarUserOptions) {
|
||||||
|
|
||||||
|
|
||||||
|
// Create the app instance.
|
||||||
|
// Here we inject into it the Quasar UI, the router & possibly the store.
|
||||||
|
const app = createAppFn(RootComponent)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
app.use(Quasar, quasarUserOptions)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const store = typeof createStore === 'function'
|
||||||
|
? await createStore({})
|
||||||
|
: createStore
|
||||||
|
|
||||||
|
|
||||||
|
app.use(store)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const router = markRaw(
|
||||||
|
typeof createRouter === 'function'
|
||||||
|
? await createRouter({store})
|
||||||
|
: createRouter
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
// make router instance available in store
|
||||||
|
|
||||||
|
store.use(({ store }) => { store.router = router })
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Expose the app, the router and the store.
|
||||||
|
// Note that we are not mounting the app here, since bootstrapping will be
|
||||||
|
// different depending on whether we are in a browser or on the server.
|
||||||
|
return {
|
||||||
|
app,
|
||||||
|
store,
|
||||||
|
router
|
||||||
|
}
|
||||||
|
}
|
||||||
158
ui/.quasar/prod-spa/client-entry.js
Normal file
158
ui/.quasar/prod-spa/client-entry.js
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/**
|
||||||
|
* THIS FILE IS GENERATED AUTOMATICALLY.
|
||||||
|
* DO NOT EDIT.
|
||||||
|
*
|
||||||
|
* You are probably looking on adding startup/initialization code.
|
||||||
|
* Use "quasar new boot <name>" and add it there.
|
||||||
|
* One boot file per concern. Then reference the file(s) in quasar.config file > boot:
|
||||||
|
* boot: ['file', ...] // do not add ".js" extension to it.
|
||||||
|
*
|
||||||
|
* Boot files are your "main.js"
|
||||||
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
import { createApp } from 'vue'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import '@quasar/extras/roboto-font/roboto-font.css'
|
||||||
|
|
||||||
|
import '@quasar/extras/material-icons/material-icons.css'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// We load Quasar stylesheet file
|
||||||
|
import 'quasar/dist/quasar.sass'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import 'src/css/app.css'
|
||||||
|
|
||||||
|
|
||||||
|
import createQuasarApp from './app.js'
|
||||||
|
import quasarUserOptions from './quasar-user-options.js'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const publicPath = `/`
|
||||||
|
|
||||||
|
|
||||||
|
async function start ({
|
||||||
|
app,
|
||||||
|
router
|
||||||
|
, store
|
||||||
|
}, bootFiles) {
|
||||||
|
|
||||||
|
let hasRedirected = false
|
||||||
|
const getRedirectUrl = url => {
|
||||||
|
try { return router.resolve(url).href }
|
||||||
|
catch (err) {}
|
||||||
|
|
||||||
|
return Object(url) === url
|
||||||
|
? null
|
||||||
|
: url
|
||||||
|
}
|
||||||
|
const redirect = url => {
|
||||||
|
hasRedirected = true
|
||||||
|
|
||||||
|
if (typeof url === 'string' && /^https?:\/\//.test(url)) {
|
||||||
|
window.location.href = url
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const href = getRedirectUrl(url)
|
||||||
|
|
||||||
|
// continue if we didn't fail to resolve the url
|
||||||
|
if (href !== null) {
|
||||||
|
window.location.href = href
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const urlPath = window.location.href.replace(window.location.origin, '')
|
||||||
|
|
||||||
|
for (let i = 0; hasRedirected === false && i < bootFiles.length; i++) {
|
||||||
|
try {
|
||||||
|
await bootFiles[i]({
|
||||||
|
app,
|
||||||
|
router,
|
||||||
|
store,
|
||||||
|
ssrContext: null,
|
||||||
|
redirect,
|
||||||
|
urlPath,
|
||||||
|
publicPath
|
||||||
|
})
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
if (err && err.url) {
|
||||||
|
redirect(err.url)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.error('[Quasar] boot error:', err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasRedirected === true) return
|
||||||
|
|
||||||
|
|
||||||
|
app.use(router)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
app.mount('#q-app')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
createQuasarApp(createApp, quasarUserOptions)
|
||||||
|
|
||||||
|
.then(app => {
|
||||||
|
// eventually remove this when Cordova/Capacitor/Electron support becomes old
|
||||||
|
const [ method, mapFn ] = Promise.allSettled !== void 0
|
||||||
|
? [
|
||||||
|
'allSettled',
|
||||||
|
bootFiles => bootFiles.map(result => {
|
||||||
|
if (result.status === 'rejected') {
|
||||||
|
console.error('[Quasar] boot error:', result.reason)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return result.value.default
|
||||||
|
})
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
'all',
|
||||||
|
bootFiles => bootFiles.map(entry => entry.default)
|
||||||
|
]
|
||||||
|
|
||||||
|
return Promise[ method ]([
|
||||||
|
|
||||||
|
import(/* webpackMode: "eager" */ 'boot/dayjs'),
|
||||||
|
|
||||||
|
import(/* webpackMode: "eager" */ 'boot/locale'),
|
||||||
|
|
||||||
|
import(/* webpackMode: "eager" */ 'boot/resizeObserverGuard')
|
||||||
|
|
||||||
|
]).then(bootFiles => {
|
||||||
|
const boot = mapFn(bootFiles).filter(entry => typeof entry === 'function')
|
||||||
|
start(app, boot)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
116
ui/.quasar/prod-spa/client-prefetch.js
Normal file
116
ui/.quasar/prod-spa/client-prefetch.js
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/**
|
||||||
|
* THIS FILE IS GENERATED AUTOMATICALLY.
|
||||||
|
* DO NOT EDIT.
|
||||||
|
*
|
||||||
|
* You are probably looking on adding startup/initialization code.
|
||||||
|
* Use "quasar new boot <name>" and add it there.
|
||||||
|
* One boot file per concern. Then reference the file(s) in quasar.config file > boot:
|
||||||
|
* boot: ['file', ...] // do not add ".js" extension to it.
|
||||||
|
*
|
||||||
|
* Boot files are your "main.js"
|
||||||
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import App from 'app/src/App.vue'
|
||||||
|
let appPrefetch = typeof App.preFetch === 'function'
|
||||||
|
? App.preFetch
|
||||||
|
: (
|
||||||
|
// Class components return the component options (and the preFetch hook) inside __c property
|
||||||
|
App.__c !== void 0 && typeof App.__c.preFetch === 'function'
|
||||||
|
? App.__c.preFetch
|
||||||
|
: false
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
function getMatchedComponents (to, router) {
|
||||||
|
const route = to
|
||||||
|
? (to.matched ? to : router.resolve(to).route)
|
||||||
|
: router.currentRoute.value
|
||||||
|
|
||||||
|
if (!route) { return [] }
|
||||||
|
|
||||||
|
const matched = route.matched.filter(m => m.components !== void 0)
|
||||||
|
|
||||||
|
if (matched.length === 0) { return [] }
|
||||||
|
|
||||||
|
return Array.prototype.concat.apply([], matched.map(m => {
|
||||||
|
return Object.keys(m.components).map(key => {
|
||||||
|
const comp = m.components[key]
|
||||||
|
return {
|
||||||
|
path: m.path,
|
||||||
|
c: comp
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addPreFetchHooks ({ router, store, publicPath }) {
|
||||||
|
// Add router hook for handling preFetch.
|
||||||
|
// Doing it after initial route is resolved so that we don't double-fetch
|
||||||
|
// the data that we already have. Using router.beforeResolve() so that all
|
||||||
|
// async components are resolved.
|
||||||
|
router.beforeResolve((to, from, next) => {
|
||||||
|
const
|
||||||
|
urlPath = window.location.href.replace(window.location.origin, ''),
|
||||||
|
matched = getMatchedComponents(to, router),
|
||||||
|
prevMatched = getMatchedComponents(from, router)
|
||||||
|
|
||||||
|
let diffed = false
|
||||||
|
const preFetchList = matched
|
||||||
|
.filter((m, i) => {
|
||||||
|
return diffed || (diffed = (
|
||||||
|
!prevMatched[i] ||
|
||||||
|
prevMatched[i].c !== m.c ||
|
||||||
|
m.path.indexOf('/:') > -1 // does it has params?
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.filter(m => m.c !== void 0 && (
|
||||||
|
typeof m.c.preFetch === 'function'
|
||||||
|
// Class components return the component options (and the preFetch hook) inside __c property
|
||||||
|
|| (m.c.__c !== void 0 && typeof m.c.__c.preFetch === 'function')
|
||||||
|
))
|
||||||
|
.map(m => m.c.__c !== void 0 ? m.c.__c.preFetch : m.c.preFetch)
|
||||||
|
|
||||||
|
|
||||||
|
if (appPrefetch !== false) {
|
||||||
|
preFetchList.unshift(appPrefetch)
|
||||||
|
appPrefetch = false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (preFetchList.length === 0) {
|
||||||
|
return next()
|
||||||
|
}
|
||||||
|
|
||||||
|
let hasRedirected = false
|
||||||
|
const redirect = url => {
|
||||||
|
hasRedirected = true
|
||||||
|
next(url)
|
||||||
|
}
|
||||||
|
const proceed = () => {
|
||||||
|
|
||||||
|
if (hasRedirected === false) { next() }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
preFetchList.reduce(
|
||||||
|
(promise, preFetch) => promise.then(() => hasRedirected === false && preFetch({
|
||||||
|
store,
|
||||||
|
currentRoute: to,
|
||||||
|
previousRoute: from,
|
||||||
|
redirect,
|
||||||
|
urlPath,
|
||||||
|
publicPath
|
||||||
|
})),
|
||||||
|
Promise.resolve()
|
||||||
|
)
|
||||||
|
.then(proceed)
|
||||||
|
.catch(e => {
|
||||||
|
console.error(e)
|
||||||
|
proceed()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
23
ui/.quasar/prod-spa/quasar-user-options.js
Normal file
23
ui/.quasar/prod-spa/quasar-user-options.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/**
|
||||||
|
* THIS FILE IS GENERATED AUTOMATICALLY.
|
||||||
|
* DO NOT EDIT.
|
||||||
|
*
|
||||||
|
* You are probably looking on adding startup/initialization code.
|
||||||
|
* Use "quasar new boot <name>" and add it there.
|
||||||
|
* One boot file per concern. Then reference the file(s) in quasar.config file > boot:
|
||||||
|
* boot: ['file', ...] // do not add ".js" extension to it.
|
||||||
|
*
|
||||||
|
* Boot files are your "main.js"
|
||||||
|
**/
|
||||||
|
|
||||||
|
import lang from 'quasar/lang/tr.js'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import {Loading,Dialog,Notify} from 'quasar'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default { config: {"notify":{"position":"top","timeout":2500}},lang,plugins: {Loading,Dialog,Notify} }
|
||||||
|
|
||||||
97
ui/quasar-dev-9102.err.log
Normal file
97
ui/quasar-dev-9102.err.log
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
npm warn Unknown project config "shamefully-hoist". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options.
|
||||||
|
npm warn Unknown project config "strict-peer-dependencies". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options.
|
||||||
|
npm warn Unknown project config "resolution-mode". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options.
|
||||||
|
|
||||||
|
App • ERROR • SPA UI in ./src/pages/ProductionProductCostingHasCostDetail.vue?vue&type=script&setup=true&lang=js
|
||||||
|
|
||||||
|
Syntax Error: TypeError: Cannot read properties of null (reading 'content')
|
||||||
|
at selectBlock (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\select.js:23:45)
|
||||||
|
at Object.loader (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\index.js:93:41)
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI in ./src/pages/ProductionProductCostingHasCostDetail.vue
|
||||||
|
|
||||||
|
Module Error (from ./node_modules/vue-loader/dist/index.js):
|
||||||
|
|
||||||
|
VueCompilerError: Element is missing end tag.
|
||||||
|
at D:\baggitekstilas\software projects\bssapp\bssapp\ui\src\pages\ProductionProductCostingHasCostDetail.vue:2617:1
|
||||||
|
2614|
|
||||||
|
2615| </script>
|
||||||
|
2616|
|
||||||
|
| ^
|
||||||
|
2617| <style scoped>
|
||||||
|
2618| .pcd-page {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
296
ui/quasar-dev-9102.out.log
Normal file
296
ui/quasar-dev-9102.out.log
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
|
||||||
|
> baggisowtfaresystem@0.0.1 dev
|
||||||
|
> quasar dev --port 9102 --hostname 127.0.0.1
|
||||||
|
|
||||||
|
|
||||||
|
.d88888b.
|
||||||
|
d88P" "Y88b
|
||||||
|
888 888
|
||||||
|
888 888 888 888 8888b. .d8888b 8888b. 888d888
|
||||||
|
888 888 888 888 "88b 88K "88b 888P"
|
||||||
|
888 Y8b 888 888 888 .d888888 "Y8888b. .d888888 888
|
||||||
|
Y88b.Y8b88P Y88b 888 888 888 X88 888 888 888
|
||||||
|
"Y888888" "Y88888 "Y888888 88888P' "Y888888 888
|
||||||
|
Y8b
|
||||||
|
|
||||||
|
App • Using quasar.config.js in "esm" format
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 16663ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 4708ms
|
||||||
|
|
||||||
|
» Reported at............... 29.04.2026 19:59:28
|
||||||
|
» App dir................... D:\baggitekstilas\software projects\bssapp\bssapp\ui
|
||||||
|
» App URL................... http://127.0.0.1:9102/
|
||||||
|
» Dev mode.................. spa
|
||||||
|
» Pkg quasar................ v2.18.6
|
||||||
|
» Pkg @quasar/app-webpack... v4.3.2
|
||||||
|
» Webpack transpiled JS..... yes (Babel)
|
||||||
|
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2252ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 4291ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 60ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 713ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 8605ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 2753ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 3927ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1249ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1265ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1128ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 4600ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2824ms
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 54ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 617ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 3332ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2947ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 80ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 611ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 4786ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 4916ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 59ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 733ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 32ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 539ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 26ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 566ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 47ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 840ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2536ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2803ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 3245ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1962ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 3271ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2830ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 39ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 605ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 22ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 253ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 3930ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1727ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 94ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 296ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 4152ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2614ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 30ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 289ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 205ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 3249ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2064ms
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 23ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 238ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
75
ui/quasar-dev-9103.err.log
Normal file
75
ui/quasar-dev-9103.err.log
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
npm warn Unknown project config "shamefully-hoist". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options.
|
||||||
|
npm warn Unknown project config "strict-peer-dependencies". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options.
|
||||||
|
npm warn Unknown project config "resolution-mode". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options.
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
282
ui/quasar-dev-9103.out.log
Normal file
282
ui/quasar-dev-9103.out.log
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
|
||||||
|
> baggisowtfaresystem@0.0.1 dev
|
||||||
|
> quasar dev --port 9103 --hostname 127.0.0.1
|
||||||
|
|
||||||
|
|
||||||
|
.d88888b.
|
||||||
|
d88P" "Y88b
|
||||||
|
888 888
|
||||||
|
888 888 888 888 8888b. .d8888b 8888b. 888d888
|
||||||
|
888 888 888 888 "88b 88K "88b 888P"
|
||||||
|
888 Y8b 888 888 888 .d888888 "Y8888b. .d888888 888
|
||||||
|
Y88b.Y8b88P Y88b 888 888 888 X88 888 888 888
|
||||||
|
"Y888888" "Y88888 "Y888888 88888P' "Y888888 888
|
||||||
|
Y8b
|
||||||
|
|
||||||
|
App • Using quasar.config.js in "esm" format
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 30114ms
|
||||||
|
|
||||||
|
» Reported at............... 29.04.2026 20:02:54
|
||||||
|
» App dir................... D:\baggitekstilas\software projects\bssapp\bssapp\ui
|
||||||
|
» App URL................... http://127.0.0.1:9103/
|
||||||
|
» Dev mode.................. spa
|
||||||
|
» Pkg quasar................ v2.18.6
|
||||||
|
» Pkg @quasar/app-webpack... v4.3.2
|
||||||
|
» Webpack transpiled JS..... yes (Babel)
|
||||||
|
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 4662ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 44ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 667ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 9844ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 4855ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1320ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1362ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1175ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 3993ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2864ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 74ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 667ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 3306ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2984ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 87ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 617ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 4879ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 5001ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 74ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 666ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 56ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 525ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 27ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 587ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 97ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 898ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2810ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2801ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 3683ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1934ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 3294ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2827ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 37ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 636ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 530ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 27ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 237ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 3974ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1801ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 57ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 286ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 3989ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2476ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 29ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 301ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 3558ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1850ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 20ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 225ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
310
ui/quasar-dev.err.log
Normal file
310
ui/quasar-dev.err.log
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
npm warn Unknown project config "shamefully-hoist". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options.
|
||||||
|
npm warn Unknown project config "strict-peer-dependencies". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options.
|
||||||
|
npm warn Unknown project config "resolution-mode". This will stop working in the next major version of npm. See `npm help npmrc` for supported config options.
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI in ./src/pages/ProductionProductCostingHasCostDetail.vue
|
||||||
|
|
||||||
|
Module Error (from ./node_modules/vue-loader/dist/index.js):
|
||||||
|
|
||||||
|
VueCompilerError: Element is missing end tag.
|
||||||
|
at D:\baggitekstilas\software projects\bssapp\bssapp\ui\src\pages\ProductionProductCostingHasCostDetail.vue:629:9
|
||||||
|
627| </div>
|
||||||
|
628|
|
||||||
|
629| <div v-else>
|
||||||
|
| ^
|
||||||
|
630| <div class="row items-center justify-end q-mb-sm" v-if="lineHistoryTargetHammaddeTuruNo">
|
||||||
|
631| <q-btn
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI in ./src/pages/ProductionProductCostingHasCostDetail.vue
|
||||||
|
|
||||||
|
Module Error (from ./node_modules/vue-loader/dist/index.js):
|
||||||
|
|
||||||
|
VueCompilerError: Element is missing end tag.
|
||||||
|
at D:\baggitekstilas\software projects\bssapp\bssapp\ui\src\pages\ProductionProductCostingHasCostDetail.vue:629:9
|
||||||
|
627| </div>
|
||||||
|
628|
|
||||||
|
629| <div v-else>
|
||||||
|
| ^
|
||||||
|
630| <div class="row items-center justify-end q-mb-sm" v-if="lineHistoryTargetHammaddeTuruNo">
|
||||||
|
631| <q-btn
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI in ./src/pages/ProductionProductCostingHasCostDetail.vue
|
||||||
|
|
||||||
|
Module Error (from ./node_modules/vue-loader/dist/index.js):
|
||||||
|
|
||||||
|
VueCompilerError: Element is missing end tag.
|
||||||
|
at D:\baggitekstilas\software projects\bssapp\bssapp\ui\src\pages\ProductionProductCostingHasCostDetail.vue:629:9
|
||||||
|
627| </div>
|
||||||
|
628|
|
||||||
|
629| <div v-else>
|
||||||
|
| ^
|
||||||
|
630| <div class="row items-center justify-end q-mb-sm" v-if="lineHistoryTargetHammaddeTuruNo">
|
||||||
|
631| <q-btn
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI in ./src/pages/ProductionProductCostingHasCostDetail.vue
|
||||||
|
|
||||||
|
Module Error (from ./node_modules/vue-loader/dist/index.js):
|
||||||
|
|
||||||
|
VueCompilerError: Element is missing end tag.
|
||||||
|
at D:\baggitekstilas\software projects\bssapp\bssapp\ui\src\pages\ProductionProductCostingHasCostDetail.vue:629:9
|
||||||
|
627| </div>
|
||||||
|
628|
|
||||||
|
629| <div v-else>
|
||||||
|
| ^
|
||||||
|
630| <div class="row items-center justify-end q-mb-sm" v-if="lineHistoryTargetHammaddeTuruNo">
|
||||||
|
631| <q-btn
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI in ./src/pages/ProductionProductCostingHasCostDetail.vue?vue&type=script&setup=true&lang=js
|
||||||
|
|
||||||
|
Syntax Error: TypeError: Cannot read properties of null (reading 'content')
|
||||||
|
at selectBlock (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\select.js:23:45)
|
||||||
|
at Object.loader (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\index.js:93:41)
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI in ./src/pages/ProductionProductCostingHasCostDetail.vue?vue&type=script&setup=true&lang=js
|
||||||
|
|
||||||
|
Syntax Error: TypeError: Cannot read properties of null (reading 'content')
|
||||||
|
at selectBlock (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\select.js:23:45)
|
||||||
|
at Object.loader (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\index.js:93:41)
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI in ./src/pages/ProductionProductCostingHasCostDetail.vue?vue&type=script&setup=true&lang=js
|
||||||
|
|
||||||
|
Syntax Error: TypeError: Cannot read properties of null (reading 'content')
|
||||||
|
at selectBlock (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\select.js:23:45)
|
||||||
|
at Object.loader (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\index.js:93:41)
|
||||||
|
|
||||||
|
Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'D:\baggitekstilas\software projects\bssapp\bssapp\ui\quasar.config.js.temporary.compiled.1777459195929.mjs' imported from D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\@quasar\app-webpack\lib\quasar-config-file.js
|
||||||
|
at finalizeResolution (node:internal/modules/esm/resolve:274:11)
|
||||||
|
at moduleResolve (node:internal/modules/esm/resolve:859:10)
|
||||||
|
at defaultResolve (node:internal/modules/esm/resolve:983:11)
|
||||||
|
at #cachedDefaultResolve (node:internal/modules/esm/loader:717:20)
|
||||||
|
at ModuleLoader.resolve (node:internal/modules/esm/loader:694:38)
|
||||||
|
at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:308:38)
|
||||||
|
at onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:650:36)
|
||||||
|
at TracingChannel.tracePromise (node:diagnostics_channel:344:14)
|
||||||
|
at ModuleLoader.import (node:internal/modules/esm/loader:649:21)
|
||||||
|
at defaultImportModuleDynamicallyForScript (node:internal/modules/esm/utils:235:31)
|
||||||
|
at importModuleDynamicallyCallback (node:internal/modules/esm/utils:257:12)
|
||||||
|
at D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\@quasar\app-webpack\lib\quasar-config-file.js:377:62
|
||||||
|
at D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\esbuild\lib\main.js:1317:33
|
||||||
|
at runOnEndCallbacks (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\esbuild\lib\main.js:1346:9)
|
||||||
|
at buildResponseToResult (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\esbuild\lib\main.js:924:7)
|
||||||
|
at D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\esbuild\lib\main.js:936:9
|
||||||
|
at new Promise (<anonymous>)
|
||||||
|
at requestCallbacks.on-end (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\esbuild\lib\main.js:935:54)
|
||||||
|
at handleRequest (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\esbuild\lib\main.js:628:17)
|
||||||
|
at handleIncomingPacket (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\esbuild\lib\main.js:653:7)
|
||||||
|
at Socket.readFromStdout (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\esbuild\lib\main.js:581:7)
|
||||||
|
at Socket.emit (node:events:519:28)
|
||||||
|
at addChunk (node:internal/streams/readable:561:12)
|
||||||
|
at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)
|
||||||
|
at Readable.push (node:internal/streams/readable:392:5)
|
||||||
|
at Pipe.onStreamRead (node:internal/stream_base_commons:189:23) {
|
||||||
|
code: 'ERR_MODULE_NOT_FOUND',
|
||||||
|
url: 'file:///D:/baggitekstilas/software%20projects/bssapp/bssapp/ui/quasar.config.js.temporary.compiled.1777459195929.mjs?t=1777474212686'
|
||||||
|
}
|
||||||
|
App • ⚠️ Importing quasar.config file results in error. Please check the Node.js stack above against the temporarily created quasar.config.js.temporary.compiled.1777459195929.mjs file and fix the original file then DELETE the temporary one ("quasar clean --qconf" can be used).
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI in ./src/pages/ProductionProductCostingHasCostDetail.vue?vue&type=script&setup=true&lang=js
|
||||||
|
|
||||||
|
Syntax Error: TypeError: Cannot read properties of null (reading 'content')
|
||||||
|
at selectBlock (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\select.js:23:45)
|
||||||
|
at Object.loader (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\index.js:93:41)
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI in ./src/pages/ProductionProductCostingHasCostDetail.vue?vue&type=script&setup=true&lang=js
|
||||||
|
|
||||||
|
Syntax Error: TypeError: Cannot read properties of null (reading 'content')
|
||||||
|
at selectBlock (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\select.js:23:45)
|
||||||
|
at Object.loader (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\index.js:93:41)
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI in ./src/pages/ProductionProductCostingHasCostDetail.vue?vue&type=script&setup=true&lang=js
|
||||||
|
|
||||||
|
Syntax Error: TypeError: Cannot read properties of null (reading 'content')
|
||||||
|
at selectBlock (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\select.js:23:45)
|
||||||
|
at Object.loader (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\index.js:93:41)
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI in ./src/pages/ProductionProductCostingHasCostDetail.vue?vue&type=script&setup=true&lang=js
|
||||||
|
|
||||||
|
Syntax Error: TypeError: Cannot read properties of null (reading 'content')
|
||||||
|
at selectBlock (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\select.js:23:45)
|
||||||
|
at Object.loader (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\index.js:93:41)
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI in ./src/pages/ProductionProductCostingHasCostDetail.vue?vue&type=script&setup=true&lang=js
|
||||||
|
|
||||||
|
Syntax Error: TypeError: Cannot read properties of null (reading 'content')
|
||||||
|
at selectBlock (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\select.js:23:45)
|
||||||
|
at Object.loader (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\index.js:93:41)
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI in ./src/pages/ProductionProductCostingHasCostDetail.vue?vue&type=script&setup=true&lang=js
|
||||||
|
|
||||||
|
Syntax Error: TypeError: Cannot read properties of null (reading 'content')
|
||||||
|
at selectBlock (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\select.js:23:45)
|
||||||
|
at Object.loader (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\index.js:93:41)
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI in ./src/pages/ProductionProductCostingHasCostDetail.vue?vue&type=script&setup=true&lang=js
|
||||||
|
|
||||||
|
Syntax Error: TypeError: Cannot read properties of null (reading 'content')
|
||||||
|
at selectBlock (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\select.js:23:45)
|
||||||
|
at Object.loader (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\index.js:93:41)
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI in ./src/pages/ProductionProductCostingHasCostDetail.vue?vue&type=script&setup=true&lang=js
|
||||||
|
|
||||||
|
Syntax Error: TypeError: Cannot read properties of null (reading 'content')
|
||||||
|
at selectBlock (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\select.js:23:45)
|
||||||
|
at Object.loader (D:\baggitekstilas\software projects\bssapp\bssapp\ui\node_modules\vue-loader\dist\index.js:93:41)
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
|
|
||||||
|
App • ERROR • SPA UI
|
||||||
|
|
||||||
|
Module not found: Can't resolve imported dependency "D:\baggitekstilas\software projects\bssapp\bssapp\ui\.quasar\dev-spa\client-entry.js"
|
||||||
|
Did you forget to install it?
|
||||||
|
|
||||||
578
ui/quasar-dev.out.log
Normal file
578
ui/quasar-dev.out.log
Normal file
@@ -0,0 +1,578 @@
|
|||||||
|
|
||||||
|
> baggisowtfaresystem@0.0.1 dev
|
||||||
|
> quasar dev --port 9100 --hostname 127.0.0.1
|
||||||
|
|
||||||
|
|
||||||
|
.d88888b.
|
||||||
|
d88P" "Y88b
|
||||||
|
888 888
|
||||||
|
888 888 888 888 8888b. .d8888b 8888b. 888d888
|
||||||
|
888 888 888 888 "88b 88K "88b 888P"
|
||||||
|
888 Y8b 888 888 888 .d888888 "Y8888b. .d888888 888
|
||||||
|
Y88b.Y8b88P Y88b 888 888 888 X88 888 888 888
|
||||||
|
"Y888888" "Y88888 "Y888888 88888P' "Y888888 888
|
||||||
|
Y8b
|
||||||
|
|
||||||
|
App • Using quasar.config.js in "esm" format
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 9782ms
|
||||||
|
|
||||||
|
» Reported at............... 29.04.2026 13:40:07
|
||||||
|
» App dir................... D:\baggitekstilas\software projects\bssapp\bssapp\ui
|
||||||
|
» App URL................... http://127.0.0.1:9100/
|
||||||
|
» Dev mode.................. spa
|
||||||
|
» Pkg quasar................ v2.18.6
|
||||||
|
» Pkg @quasar/app-webpack... v4.3.2
|
||||||
|
» Webpack transpiled JS..... yes (Babel)
|
||||||
|
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 252ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 32ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 219ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 907ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 32ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 212ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 459ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 711ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 578ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 987ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 618ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 433ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 431ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1103ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2147ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 340ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 453ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 721ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 392ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 685ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1348ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1650ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 22ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 196ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 111ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 52ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 224ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2054ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 381ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 656ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 61ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 420ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 355ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 2573ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 35ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 355ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 2168ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 58ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 445ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 1914ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 55ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 443ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 2288ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 56ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 348ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 38ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 339ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 277ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 47ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 489ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 3903ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2020ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 4095ms
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 42ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 661ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 8717ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 5079ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2476ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1230ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1203ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1129ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 5848ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2848ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 115ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 612ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 3207ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2894ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 73ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 451ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 256ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 5443ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 4432ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 56ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 774ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 35ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 509ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 31ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 576ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 50ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 833ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2630ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2299ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 3191ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1905ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 3218ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2310ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 37ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 624ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 647ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 26ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 234ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 4120ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1714ms
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 26ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 243ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 219ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 4317ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 2456ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 18ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 251ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 3959ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 1742ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled by Webpack with errors • 19ms
|
||||||
|
|
||||||
|
App • COMPILATION FAILED • Please check the log above for details.
|
||||||
|
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
|
|
||||||
|
App • The quasar.config file (or its dependencies) changed. Reading it again...
|
||||||
|
App • TIP • 🚀 You are using the --port parameter. It is recommended to use a different devServer port for each Quasar mode to avoid browser cache issues
|
||||||
|
App • Scheduled to apply quasar.config changes in 550ms
|
||||||
|
App • WAIT • Compiling of "SPA UI" by Webpack in progress...
|
||||||
|
App • DONE • "SPA UI" compiled with success by Webpack • 226ms
|
||||||
|
App • Applying quasar.config file changes...
|
||||||
@@ -325,7 +325,7 @@ const menuItems = [
|
|||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
label: 'Fiyatlandırma',
|
label: 'Fiyatlandırma/Maliyetlendirme',
|
||||||
icon: 'request_quote',
|
icon: 'request_quote',
|
||||||
|
|
||||||
children: [
|
children: [
|
||||||
@@ -333,6 +333,16 @@ const menuItems = [
|
|||||||
label: 'Ürün Fiyatlandırma',
|
label: 'Ürün Fiyatlandırma',
|
||||||
to: '/app/pricing/product-pricing',
|
to: '/app/pricing/product-pricing',
|
||||||
permission: 'order:view'
|
permission: 'order:view'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Üretim'den Ürün Maliyetlendirme",
|
||||||
|
to: '/app/pricing/production-product-costing',
|
||||||
|
permission: 'order:view'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Maliyet Parça Eşleştirme',
|
||||||
|
to: '/app/pricing/production-product-costing/maliyet-parca-eslestirme',
|
||||||
|
permission: 'order:view'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
110
ui/src/pages/ProductionProductCosting.vue
Normal file
110
ui/src/pages/ProductionProductCosting.vue
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
<template>
|
||||||
|
<q-page v-if="canReadOrder" class="costing-gateway-page flex flex-center">
|
||||||
|
<div class="gateway-container">
|
||||||
|
<div class="gateway-header">
|
||||||
|
<div class="text-h5">Üretim'den Ürün Maliyetlendirme</div>
|
||||||
|
<div class="text-subtitle2 text-grey-7">
|
||||||
|
İşlem seçerek maliyetlendirme ekranına geçin
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="gateway-actions row q-col-gutter-lg q-mt-lg">
|
||||||
|
<q-card
|
||||||
|
class="gateway-card cursor-pointer"
|
||||||
|
flat
|
||||||
|
bordered
|
||||||
|
@click="goHasCostProducts"
|
||||||
|
>
|
||||||
|
<q-card-section class="text-center">
|
||||||
|
<q-icon name="fact_check" size="48px" color="primary" />
|
||||||
|
<div class="text-h6 q-mt-sm">Mevcut Maliyeti Olan Ürünleri Göster</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
|
||||||
|
<q-card
|
||||||
|
class="gateway-card cursor-pointer"
|
||||||
|
flat
|
||||||
|
bordered
|
||||||
|
@click="goNoCostProducts"
|
||||||
|
>
|
||||||
|
<q-card-section class="text-center">
|
||||||
|
<q-icon name="price_change" size="48px" color="primary" />
|
||||||
|
<div class="text-h6 q-mt-sm">Maliyeti Olmayan Ürünler İçin Maliyet Oluştur</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
|
||||||
|
<q-card
|
||||||
|
class="gateway-card cursor-pointer"
|
||||||
|
flat
|
||||||
|
bordered
|
||||||
|
@click="goMTBolumMapping"
|
||||||
|
>
|
||||||
|
<q-card-section class="text-center">
|
||||||
|
<q-icon name="hub" size="48px" color="primary" />
|
||||||
|
<div class="text-h6 q-mt-sm">Maliyet Parça Eşleştirme</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-page>
|
||||||
|
|
||||||
|
<q-page v-else class="q-pa-md flex flex-center">
|
||||||
|
<div class="text-negative text-subtitle1">
|
||||||
|
Bu module erisim yetkiniz yok.
|
||||||
|
</div>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { usePermission } from 'src/composables/usePermission'
|
||||||
|
|
||||||
|
const { canRead } = usePermission()
|
||||||
|
const canReadOrder = canRead('order')
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
function goHasCostProducts () {
|
||||||
|
if (!canReadOrder.value) return
|
||||||
|
router.push({ name: 'production-product-costing-has-cost' })
|
||||||
|
}
|
||||||
|
|
||||||
|
function goNoCostProducts () {
|
||||||
|
if (!canReadOrder.value) return
|
||||||
|
router.push({ name: 'production-product-costing-no-cost' })
|
||||||
|
}
|
||||||
|
|
||||||
|
function goMTBolumMapping () {
|
||||||
|
if (!canReadOrder.value) return
|
||||||
|
router.push({ name: 'production-product-costing-maliyet-parca-eslestirme' })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.costing-gateway-page {
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gateway-container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1000px;
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gateway-header {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gateway-actions {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gateway-card {
|
||||||
|
width: 360px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.gateway-card:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
478
ui/src/pages/ProductionProductCostingHasCost.vue
Normal file
478
ui/src/pages/ProductionProductCostingHasCost.vue
Normal file
@@ -0,0 +1,478 @@
|
|||||||
|
<template>
|
||||||
|
<q-page v-if="canReadOrder" class="npc-page">
|
||||||
|
<div class="ol-filter-bar npc-filter-bar">
|
||||||
|
<div class="npc-filter-row">
|
||||||
|
<q-input
|
||||||
|
v-model="filters.search"
|
||||||
|
class="npc-filter-input npc-search"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
clearable
|
||||||
|
debounce="300"
|
||||||
|
label="Arama (Maliyet No / Urun Kodu / Urun Adi / Aciklama)"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<q-icon name="search" />
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
|
||||||
|
<div class="ol-filter-actions npc-filter-actions">
|
||||||
|
<q-btn
|
||||||
|
label="Temizle"
|
||||||
|
icon="clear"
|
||||||
|
color="grey-7"
|
||||||
|
flat
|
||||||
|
:disable="loading"
|
||||||
|
@click="clearFilters"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
label="Kolon Filtreleri"
|
||||||
|
icon="filter_alt_off"
|
||||||
|
color="grey-7"
|
||||||
|
flat
|
||||||
|
:disable="loading"
|
||||||
|
@click="clearAllColumnFilters"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
label="Yenile"
|
||||||
|
icon="refresh"
|
||||||
|
color="primary"
|
||||||
|
:loading="loading"
|
||||||
|
@click="fetchRows"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<q-table
|
||||||
|
title="Mevcut Maliyeti Olan Urunler"
|
||||||
|
class="ol-table npc-table"
|
||||||
|
flat
|
||||||
|
bordered
|
||||||
|
dense
|
||||||
|
virtual-scroll
|
||||||
|
:virtual-scroll-item-size="34"
|
||||||
|
table-style="max-height: calc(100vh - 190px)"
|
||||||
|
separator="cell"
|
||||||
|
row-key="__rowKey"
|
||||||
|
:rows="rows"
|
||||||
|
:columns="columns"
|
||||||
|
:loading="loading"
|
||||||
|
no-data-label="Kayit bulunamadi"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
v-model:pagination="tablePagination"
|
||||||
|
hide-bottom
|
||||||
|
>
|
||||||
|
<template #header-cell="props">
|
||||||
|
<q-th :props="props">
|
||||||
|
<div class="npc-header-cell">
|
||||||
|
<div class="npc-head-wrap-3">{{ props.col.label }}</div>
|
||||||
|
<q-btn
|
||||||
|
v-if="props.col.name !== 'open'"
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
size="sm"
|
||||||
|
icon="filter_alt"
|
||||||
|
:color="isColumnFilterActive(props.col.name) ? 'primary' : 'grey-6'"
|
||||||
|
>
|
||||||
|
<q-menu class="npc-filter-menu" fit>
|
||||||
|
<div class="npc-filter-menu-content">
|
||||||
|
<div class="text-caption text-weight-bold q-mb-sm">{{ props.col.label }}</div>
|
||||||
|
<q-input
|
||||||
|
v-model="getColumnFilter(props.col.name).text"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
clearable
|
||||||
|
label="Icerir"
|
||||||
|
/>
|
||||||
|
<q-select
|
||||||
|
v-model="getColumnFilter(props.col.name).selected"
|
||||||
|
class="q-mt-sm"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
multiple
|
||||||
|
use-chips
|
||||||
|
use-input
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
:options="columnDistinctOptions[props.col.name] || []"
|
||||||
|
label="Deger Sec"
|
||||||
|
/>
|
||||||
|
<div class="row justify-end q-gutter-sm q-mt-sm">
|
||||||
|
<q-btn dense flat color="grey-7" label="Temizle" @click="clearColumnFilter(props.col.name)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-menu>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</q-th>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell="props">
|
||||||
|
<q-td v-if="props.col.name === 'open'" :props="props" class="text-center">
|
||||||
|
<q-btn
|
||||||
|
icon="open_in_new"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
@click="openRow(props.row)"
|
||||||
|
>
|
||||||
|
<q-tooltip>Ac</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
</q-td>
|
||||||
|
<q-td v-else :props="props" class="npc-wrap-col">
|
||||||
|
<div class="npc-wrap-3" :title="String(props.value || '')">{{ props.value }}</div>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
</q-table>
|
||||||
|
|
||||||
|
<q-banner v-if="error" class="bg-red text-white q-mt-sm">
|
||||||
|
Hata: {{ error }}
|
||||||
|
</q-banner>
|
||||||
|
</q-page>
|
||||||
|
|
||||||
|
<q-page v-else class="q-pa-md flex flex-center">
|
||||||
|
<div class="text-negative text-subtitle1">
|
||||||
|
Bu module erisim yetkiniz yok.
|
||||||
|
</div>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { usePermission } from 'src/composables/usePermission'
|
||||||
|
import { get, extractApiErrorDetail } from 'src/services/api'
|
||||||
|
|
||||||
|
const { canRead } = usePermission()
|
||||||
|
const canReadOrder = canRead('order')
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const error = ref('')
|
||||||
|
const allRows = ref([])
|
||||||
|
const tablePagination = ref({
|
||||||
|
sortBy: 'Tarihi',
|
||||||
|
descending: true,
|
||||||
|
page: 1,
|
||||||
|
rowsPerPage: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
const filters = reactive({
|
||||||
|
search: ''
|
||||||
|
})
|
||||||
|
const dateColumns = new Set(['Tarihi', 'dteKayitTarihi', 'dteGuncellemeTarihi', 'SonSiparisTarihi'])
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ name: 'open', label: '', field: 'open', align: 'center', sortable: false, style: 'width:3%', headerStyle: 'width:3%' },
|
||||||
|
{ name: 'UretimSekli', label: 'Uretim Sekli', field: 'UretimSekli', align: 'left', sortable: true, style: 'width:9%', headerStyle: 'width:9%' },
|
||||||
|
{ name: 'nOnMLNo', label: 'nOnMLNo', field: 'nOnMLNo', align: 'left', sortable: true, style: 'width:6%', headerStyle: 'width:6%' },
|
||||||
|
{ name: 'UrunKodu', label: 'UrunKodu', field: 'UrunKodu', align: 'left', sortable: true, style: 'width:7%', headerStyle: 'width:7%' },
|
||||||
|
{ name: 'UrunAdi', label: 'UrunAdi', field: 'UrunAdi', align: 'left', sortable: true, style: 'width:8%', headerStyle: 'width:8%' },
|
||||||
|
{ name: 'Tarihi', label: 'Tarihi', field: 'Tarihi', align: 'center', sortable: true, format: val => formatDateTR(val), style: 'width:6%', headerStyle: 'width:6%' },
|
||||||
|
{ name: 'dteKayitTarihi', label: 'dteKayitTarihi', field: 'dteKayitTarihi', align: 'center', sortable: true, format: val => formatDateTR(val), style: 'width:6%', headerStyle: 'width:6%' },
|
||||||
|
{ name: 'sKullaniciAdi', label: 'sKullaniciAdi', field: 'sKullaniciAdi', align: 'left', sortable: true, style: 'width:7%', headerStyle: 'width:7%' },
|
||||||
|
{ name: 'lTutarTL', label: 'lTutarTL', field: 'lTutarTL', align: 'right', sortable: true, format: val => formatMoney(val), style: 'width:6%', headerStyle: 'width:6%' },
|
||||||
|
{ name: 'lTutarUSD', label: 'lTutarUSD', field: 'lTutarUSD', align: 'right', sortable: true, format: val => formatMoney(val), style: 'width:6%', headerStyle: 'width:6%' },
|
||||||
|
{ name: 'lTutarEURO', label: 'lTutarEURO', field: 'lTutarEURO', align: 'right', sortable: true, format: val => formatMoney(val), style: 'width:6%', headerStyle: 'width:6%' },
|
||||||
|
{ name: 'dteGuncellemeTarihi', label: 'dteGuncellemeTarihi', field: 'dteGuncellemeTarihi', align: 'center', sortable: true, format: val => formatDateTR(val), style: 'width:7%', headerStyle: 'width:7%' },
|
||||||
|
{ name: 'sGuncellemeKullaniciAdi', label: 'sGuncellemeKullaniciAdi', field: 'sGuncellemeKullaniciAdi', align: 'left', sortable: true, style: 'width:7%', headerStyle: 'width:7%' },
|
||||||
|
{ name: 'nUrtReceteID', label: 'nUrtReceteID', field: 'nUrtReceteID', align: 'left', sortable: true, style: 'width:6%', headerStyle: 'width:6%' },
|
||||||
|
{ name: 'sAciklama', label: 'sAciklama', field: 'sAciklama', align: 'left', sortable: true, style: 'width:8%', headerStyle: 'width:8%' },
|
||||||
|
{ name: 'SonSiparisTarihi', label: 'SonSiparisTarihi', field: 'SonSiparisTarihi', align: 'center', sortable: true, format: val => formatDateTR(val), style: 'width:7%', headerStyle: 'width:7%' },
|
||||||
|
{ name: 'MaliyetDurumu', label: 'MaliyetDurumu', field: 'MaliyetDurumu', align: 'left', sortable: true, style: 'width:8%', headerStyle: 'width:8%' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const columnFilters = reactive({})
|
||||||
|
|
||||||
|
function getColumnFilter (name) {
|
||||||
|
if (!columnFilters[name]) {
|
||||||
|
columnFilters[name] = {
|
||||||
|
text: '',
|
||||||
|
selected: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return columnFilters[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDateTR (value) {
|
||||||
|
const s = String(value || '').trim()
|
||||||
|
if (!s) return ''
|
||||||
|
const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(s)
|
||||||
|
if (!m) return s
|
||||||
|
return `${m[3]}.${m[2]}.${m[1]}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatMoney (value) {
|
||||||
|
return Number(value || 0).toLocaleString('tr-TR', {
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const columnDistinctOptions = computed(() => {
|
||||||
|
const optionsByColumn = {}
|
||||||
|
|
||||||
|
for (const col of columns) {
|
||||||
|
if (col.name === 'open') continue
|
||||||
|
const set = new Set()
|
||||||
|
|
||||||
|
for (const row of allRows.value) {
|
||||||
|
const val = getColumnComparableValue(row, col.name)
|
||||||
|
if (val) set.add(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
optionsByColumn[col.name] = Array.from(set)
|
||||||
|
.sort((a, b) => a.localeCompare(b, 'tr'))
|
||||||
|
.map(v => ({ label: v, value: v }))
|
||||||
|
}
|
||||||
|
|
||||||
|
return optionsByColumn
|
||||||
|
})
|
||||||
|
|
||||||
|
const rows = computed(() => {
|
||||||
|
let result = allRows.value
|
||||||
|
|
||||||
|
for (const col of columns) {
|
||||||
|
if (col.name === 'open') continue
|
||||||
|
|
||||||
|
const cf = getColumnFilter(col.name)
|
||||||
|
const text = String(cf.text || '').trim().toLowerCase()
|
||||||
|
const selected = Array.isArray(cf.selected) ? cf.selected : []
|
||||||
|
|
||||||
|
if (!text && selected.length === 0) continue
|
||||||
|
|
||||||
|
result = result.filter((row) => {
|
||||||
|
const value = getColumnComparableValue(row, col.name)
|
||||||
|
const valueLC = value.toLowerCase()
|
||||||
|
|
||||||
|
if (text && !valueLC.includes(text)) return false
|
||||||
|
if (selected.length > 0 && !selected.includes(value)) return false
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
|
function getColumnComparableValue (row, colName) {
|
||||||
|
if (row?.__cmp?.[colName] !== undefined) return row.__cmp[colName]
|
||||||
|
if (dateColumns.has(colName)) return formatDateTR(row?.[colName])
|
||||||
|
return String(row?.[colName] ?? '').trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildComparableMap (row) {
|
||||||
|
const cmp = {}
|
||||||
|
for (const col of columns) {
|
||||||
|
if (col.name === 'open') continue
|
||||||
|
cmp[col.name] = dateColumns.has(col.name)
|
||||||
|
? formatDateTR(row?.[col.name])
|
||||||
|
: String(row?.[col.name] ?? '').trim()
|
||||||
|
}
|
||||||
|
return cmp
|
||||||
|
}
|
||||||
|
|
||||||
|
function isColumnFilterActive (name) {
|
||||||
|
const cf = getColumnFilter(name)
|
||||||
|
return !!String(cf.text || '').trim() || (Array.isArray(cf.selected) && cf.selected.length > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearColumnFilter (name) {
|
||||||
|
const cf = getColumnFilter(name)
|
||||||
|
cf.text = ''
|
||||||
|
cf.selected = []
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAllColumnFilters () {
|
||||||
|
for (const col of columns) {
|
||||||
|
if (col.name === 'open') continue
|
||||||
|
clearColumnFilter(col.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let searchTimer = null
|
||||||
|
watch(
|
||||||
|
() => filters.search,
|
||||||
|
() => {
|
||||||
|
clearTimeout(searchTimer)
|
||||||
|
searchTimer = setTimeout(() => {
|
||||||
|
fetchRows()
|
||||||
|
}, 400)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
async function fetchRows () {
|
||||||
|
loading.value = true
|
||||||
|
error.value = ''
|
||||||
|
try {
|
||||||
|
const data = await get('/pricing/production-product-costing/has-cost-products', {
|
||||||
|
search: filters.search || '',
|
||||||
|
offset: 0,
|
||||||
|
limit: 300
|
||||||
|
})
|
||||||
|
|
||||||
|
const list = Array.isArray(data) ? data : []
|
||||||
|
const sortedList = list.slice().sort((a, b) => {
|
||||||
|
const aDate = Date.parse(String(a?.Tarihi || ''))
|
||||||
|
const bDate = Date.parse(String(b?.Tarihi || ''))
|
||||||
|
const aTs = Number.isNaN(aDate) ? 0 : aDate
|
||||||
|
const bTs = Number.isNaN(bDate) ? 0 : bDate
|
||||||
|
return bTs - aTs
|
||||||
|
})
|
||||||
|
|
||||||
|
allRows.value = sortedList.map((x, i) => ({
|
||||||
|
__rowKey: `${x?.nOnMLNo || ''}-${x?.UrunKodu || ''}-${i}`,
|
||||||
|
__cmp: buildComparableMap(x),
|
||||||
|
...x
|
||||||
|
}))
|
||||||
|
|
||||||
|
tablePagination.value = {
|
||||||
|
...tablePagination.value,
|
||||||
|
sortBy: 'Tarihi',
|
||||||
|
descending: true,
|
||||||
|
page: 1
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
error.value = await extractApiErrorDetail(err)
|
||||||
|
allRows.value = []
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearFilters () {
|
||||||
|
filters.search = ''
|
||||||
|
clearAllColumnFilters()
|
||||||
|
fetchRows()
|
||||||
|
}
|
||||||
|
|
||||||
|
function openRow (row) {
|
||||||
|
const urunKodu = String(row?.UrunKodu || '').trim()
|
||||||
|
if (!urunKodu) return
|
||||||
|
|
||||||
|
router.push({
|
||||||
|
name: 'production-product-costing-has-cost-history',
|
||||||
|
query: { urun_kodu: urunKodu }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!canReadOrder.value) return
|
||||||
|
fetchRows()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.npc-page {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-filter-bar {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-filter-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-filter-input {
|
||||||
|
min-width: 118px;
|
||||||
|
width: 136px;
|
||||||
|
flex: 0 0 136px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-search {
|
||||||
|
min-width: 240px;
|
||||||
|
max-width: 420px;
|
||||||
|
flex: 1 1 360px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-filter-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-filter-menu {
|
||||||
|
min-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-filter-menu-content {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-table :deep(.q-table thead th) {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 3px 4px;
|
||||||
|
white-space: normal !important;
|
||||||
|
vertical-align: top !important;
|
||||||
|
line-height: 1.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-table :deep(.q-table tbody td) {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 2px 4px;
|
||||||
|
white-space: normal !important;
|
||||||
|
vertical-align: top !important;
|
||||||
|
line-height: 1.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-table :deep(.q-table) {
|
||||||
|
width: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-wrap-col {
|
||||||
|
white-space: normal !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-header-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 4px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-head-wrap-3 {
|
||||||
|
min-width: 0;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
overflow: hidden;
|
||||||
|
line-height: 1.15;
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-wrap-3 {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
overflow: hidden;
|
||||||
|
line-height: 1.15;
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1440px) {
|
||||||
|
.npc-filter-row {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-filter-actions {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-filter-input {
|
||||||
|
flex: 1 1 140px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
3474
ui/src/pages/ProductionProductCostingHasCostDetail.vue
Normal file
3474
ui/src/pages/ProductionProductCostingHasCostDetail.vue
Normal file
@@ -0,0 +1,3474 @@
|
|||||||
|
<template>
|
||||||
|
<q-page v-if="canReadOrder" class="pcd-page" :style="pageVars">
|
||||||
|
<div ref="stickyStackRef" class="sticky-stack pcd-sticky-stack">
|
||||||
|
<div ref="saveToolbarRef" class="save-toolbar pcd-save-toolbar q-px-md">
|
||||||
|
<div class="pcd-toolbar-row">
|
||||||
|
<div class="pcd-toolbar-left">
|
||||||
|
<div class="pcd-toolbar-title">Maliyet Detay Sayfasi</div>
|
||||||
|
<div v-if="detailHeader && !detailLoading" class="pcd-toolbar-summary">
|
||||||
|
<div class="pcd-toolbar-pill pcd-toolbar-pill-emphasis">
|
||||||
|
<span class="pcd-toolbar-pill-label">USD</span>
|
||||||
|
<span class="pcd-toolbar-pill-value">{{ formatMoney(toolbarSummary.usdTotal) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pcd-toolbar-pill pcd-toolbar-pill-emphasis">
|
||||||
|
<span class="pcd-toolbar-pill-label">EUR</span>
|
||||||
|
<span class="pcd-toolbar-pill-value">{{ formatMoney(toolbarSummary.eurTotal) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pcd-toolbar-pill pcd-toolbar-pill-emphasis">
|
||||||
|
<span class="pcd-toolbar-pill-label">GBP</span>
|
||||||
|
<span class="pcd-toolbar-pill-value">{{ formatMoney(toolbarSummary.gbpTotal) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pcd-toolbar-pill pcd-toolbar-pill-neutral">
|
||||||
|
<span class="pcd-toolbar-pill-label">USD Kur</span>
|
||||||
|
<span class="pcd-toolbar-pill-value">{{ formatMoney(exchangeRates.usdRate) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pcd-toolbar-pill pcd-toolbar-pill-neutral">
|
||||||
|
<span class="pcd-toolbar-pill-label">EUR Kur</span>
|
||||||
|
<span class="pcd-toolbar-pill-value">{{ formatMoney(exchangeRates.eurRate) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="pcd-toolbar-pill pcd-toolbar-pill-neutral">
|
||||||
|
<span class="pcd-toolbar-pill-label">GBP Kur</span>
|
||||||
|
<span class="pcd-toolbar-pill-value">{{ formatMoney(exchangeRates.gbpRate) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="pcd-toolbar-actions">
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
color="grey-7"
|
||||||
|
class="pcd-toolbar-btn"
|
||||||
|
:label="headerInfoCollapsed ? 'HEADER GOSTER' : 'HEADER DARALT'"
|
||||||
|
:icon="headerInfoCollapsed ? 'expand_more' : 'expand_less'"
|
||||||
|
@click="toggleHeaderInfo"
|
||||||
|
/>
|
||||||
|
<q-btn icon="arrow_back" label="Geri" dense flat color="grey-8" class="pcd-toolbar-btn" @click="goBack" />
|
||||||
|
<q-btn label="Yenile" icon="refresh" dense color="primary" class="pcd-toolbar-btn" :loading="detailLoading" @click="fetchDetail" />
|
||||||
|
<q-btn
|
||||||
|
label="Toplu Fiyat Cagir"
|
||||||
|
icon="playlist_add_check"
|
||||||
|
dense
|
||||||
|
color="secondary"
|
||||||
|
outline
|
||||||
|
class="pcd-toolbar-btn"
|
||||||
|
:loading="bulkPriceLoading"
|
||||||
|
:disable="!detailHeader || detailLoading || saveLoading || bulkPriceLoading"
|
||||||
|
@click="fetchBulkItemPrices"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
label="SATIR EKLE"
|
||||||
|
dense
|
||||||
|
color="secondary"
|
||||||
|
icon="add"
|
||||||
|
class="pcd-toolbar-btn"
|
||||||
|
:disable="!detailHeader || detailLoading || saveLoading || bulkPriceLoading"
|
||||||
|
@click="openNewRowDialog"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
label="Kaydet"
|
||||||
|
icon="save"
|
||||||
|
dense
|
||||||
|
color="primary"
|
||||||
|
outline
|
||||||
|
class="pcd-toolbar-btn"
|
||||||
|
:loading="saveLoading"
|
||||||
|
:disable="!detailHeader || detailLoading || saveLoading || bulkPriceLoading"
|
||||||
|
@click="saveChanges"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<q-banner v-if="detailError" class="bg-red text-white q-mb-md">
|
||||||
|
Hata: {{ detailError }}
|
||||||
|
</q-banner>
|
||||||
|
|
||||||
|
<div v-if="detailHeader && !detailLoading && !headerInfoCollapsed" class="filter-bar pcd-detail-header-bar q-mx-md q-mb-md">
|
||||||
|
<div class="row q-col-gutter-sm">
|
||||||
|
<div class="col-12 col-md-3">
|
||||||
|
<q-input
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
readonly
|
||||||
|
label="Maliyet Tarihi"
|
||||||
|
:model-value="formatDateTR(costDate)"
|
||||||
|
class="pcd-emphasis-field-alt"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<q-icon name="event" class="cursor-pointer">
|
||||||
|
<q-popup-proxy cover transition-show="scale" transition-hide="scale">
|
||||||
|
<q-date v-model="costDate" mask="YYYY-MM-DD" locale="tr-TR" />
|
||||||
|
</q-popup-proxy>
|
||||||
|
</q-icon>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-3">
|
||||||
|
<q-select
|
||||||
|
v-if="!isNoCostDetail"
|
||||||
|
v-model="detailHeader.UretimSekliID"
|
||||||
|
:options="productionTypes"
|
||||||
|
option-value="id"
|
||||||
|
option-label="aciklama"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
label="Uretim Sekli"
|
||||||
|
class="pcd-emphasis-field-alt"
|
||||||
|
@update:model-value="onUretimSekliChange"
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
v-else
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
readonly
|
||||||
|
label="Uretim Sekli"
|
||||||
|
:model-value="detailHeader.UretimSekli || '-'"
|
||||||
|
class="pcd-emphasis-field-alt"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<div class="row q-col-gutter-xs">
|
||||||
|
<div class="col-4">
|
||||||
|
<q-input dense filled readonly label="USD Kuru" :model-value="formatMoney(exchangeRates.usdRate)" class="pcd-emphasis-field-alt" />
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<q-input dense filled readonly label="EUR Kuru" :model-value="formatMoney(exchangeRates.eurRate)" class="pcd-emphasis-field-alt" />
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<q-input dense filled readonly label="GBP Kuru" :model-value="formatMoney(exchangeRates.gbpRate)" class="pcd-emphasis-field-alt" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12 col-md-4">
|
||||||
|
<q-input dense filled readonly label="Uretimi Yapan Firma" :model-value="detailHeader.UretimiYapanFirma || '-'" />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-4">
|
||||||
|
<q-input dense filled readonly label="2.Firma" :model-value="detailHeader.SonIsEmriVeren || '-'" />
|
||||||
|
</div>
|
||||||
|
<div class="col-6 col-md-2">
|
||||||
|
<q-input dense filled readonly label="nOnMLNo" :model-value="detailHeader.nOnMLNo || '-'" />
|
||||||
|
</div>
|
||||||
|
<div class="col-6 col-md-2">
|
||||||
|
<q-input dense filled readonly label="UrunKodu" :model-value="detailHeader.UrunKodu || '-'" />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-3">
|
||||||
|
<q-input dense filled readonly label="UrunAdi" :model-value="detailHeader.UrunAdi || '-'" />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-3">
|
||||||
|
<q-input dense filled readonly label="Urun Ana Grubu" :model-value="detailHeader.UrunAnaGrubu || '-'" />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-3">
|
||||||
|
<q-input dense filled readonly label="Urun Alt Grubu" :model-value="detailHeader.UrunAltGrubu || '-'" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-6 col-md-2">
|
||||||
|
<q-input dense filled readonly label="sKullaniciAdi" :model-value="detailHeader.sKullaniciAdi || '-'" />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-3">
|
||||||
|
<q-input dense filled readonly label="Son Guncelleme Tarihi" :model-value="formatDateTR(detailHeader.dteGuncellemeTarihi)" />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-2">
|
||||||
|
<q-input dense filled readonly label="sGuncellemeKullaniciAdi" :model-value="detailHeader.sGuncellemeKullaniciAdi || '-'" />
|
||||||
|
</div>
|
||||||
|
<div class="col-6 col-md-2">
|
||||||
|
<q-input dense filled readonly label="nUrtReceteID" :model-value="detailHeader.nUrtReceteID || '-'" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="!isNoCostDetail && partSummary && partSummary.length > 0" class="col-12">
|
||||||
|
<div class="pcd-part-summary-card">
|
||||||
|
<div class="pcd-part-summary-title">Parça Bazlı Maliyet Özetleri</div>
|
||||||
|
<q-markup-table dense flat bordered separator="cell" class="pcd-part-summary-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="text-left">Parça</th>
|
||||||
|
<th class="text-right">TRY</th>
|
||||||
|
<th class="text-right">USD</th>
|
||||||
|
<th class="text-right">EUR</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="ps in partSummary" :key="ps.name">
|
||||||
|
<td class="text-left text-weight-bold">{{ ps.name }}</td>
|
||||||
|
<td class="text-right">{{ formatMoney(ps.try) }}</td>
|
||||||
|
<td class="text-right">{{ formatMoney(ps.usd) }}</td>
|
||||||
|
<td class="text-right">{{ formatMoney(ps.eur) }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</q-markup-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-if="detailHeader && !detailLoading && headerInfoCollapsed" class="filter-bar pcd-detail-header-bar q-mx-md q-mb-md">
|
||||||
|
<div class="row q-col-gutter-sm">
|
||||||
|
<div class="col-6 col-md-2">
|
||||||
|
<q-input dense filled readonly label="nOnMLNo" :model-value="detailHeader.nOnMLNo || '-'" />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-3">
|
||||||
|
<q-input dense filled readonly label="Uretimi Yapan Firma" :model-value="detailHeader.UretimiYapanFirma || '-'" />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-3">
|
||||||
|
<q-input dense filled readonly label="2.Firma" :model-value="detailHeader.SonIsEmriVeren || '-'" />
|
||||||
|
</div>
|
||||||
|
<div class="col-6 col-md-2">
|
||||||
|
<q-input dense filled readonly label="UrunKodu" :model-value="detailHeader.UrunKodu || '-'" />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-2">
|
||||||
|
<q-input dense filled readonly label="Uretim Sekli" :model-value="formatUretimSekli(detailHeader)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="detailLoading" class="row justify-center q-pa-lg">
|
||||||
|
<q-spinner color="primary" size="36px" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="detailGroups.length === 0" class="text-grey-7 q-pa-md">
|
||||||
|
Kayit bulunamadi.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="column q-gutter-md pcd-content-body">
|
||||||
|
<div
|
||||||
|
v-for="(grp, gi) in detailGroups"
|
||||||
|
:key="groupKey(grp, gi)"
|
||||||
|
class="pcd-group-card"
|
||||||
|
>
|
||||||
|
<div class="order-sub-header pcd-sub-header">
|
||||||
|
<div class="sub-left">
|
||||||
|
{{ grp.sAciklama3 || 'TANIMSIZ' }}
|
||||||
|
</div>
|
||||||
|
<div class="sub-right pcd-sub-right-clickable" @click="toggleGroup(grp, gi)">
|
||||||
|
Grup Toplami TRY: {{ formatBarMoney(resolveGroupTRYTutar(grp)) }} | USD: {{ formatBarMoney(resolveGroupUSDTutar(grp)) }}
|
||||||
|
<q-icon
|
||||||
|
:name="isGroupOpen(grp, gi) ? 'expand_less' : 'expand_more'"
|
||||||
|
size="18px"
|
||||||
|
class="q-ml-sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<q-table
|
||||||
|
v-if="isGroupOpen(grp, gi)"
|
||||||
|
class="pcd-detail-table"
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
bordered
|
||||||
|
separator="cell"
|
||||||
|
row-key="__rowKey"
|
||||||
|
:rows="grp.items"
|
||||||
|
:columns="detailColumns"
|
||||||
|
:table-row-class-fn="resolveDetailRowClass"
|
||||||
|
@row-click="onDetailRowClick"
|
||||||
|
hide-bottom
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
>
|
||||||
|
<template #header-cell-lMiktar="props">
|
||||||
|
<q-th :props="props" class="pcd-entry-header">
|
||||||
|
{{ props.col.label }}
|
||||||
|
</q-th>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #header-cell-inputPrice="props">
|
||||||
|
<q-th :props="props" class="pcd-entry-header">
|
||||||
|
{{ props.col.label }}
|
||||||
|
</q-th>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #header-cell-inputPricePrBr="props">
|
||||||
|
<q-th :props="props" class="pcd-entry-header">
|
||||||
|
{{ props.col.label }}
|
||||||
|
</q-th>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #header-cell-maliyeteDahil="props">
|
||||||
|
<q-th :props="props" class="pcd-secondary-header">
|
||||||
|
{{ props.col.label }}
|
||||||
|
</q-th>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #header-cell-cmPriceType="props">
|
||||||
|
<q-th :props="props" class="pcd-secondary-header">
|
||||||
|
{{ props.col.label }}
|
||||||
|
</q-th>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-actions="props">
|
||||||
|
<q-td :props="props" class="q-gutter-xs no-wrap">
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
size="sm"
|
||||||
|
color="primary"
|
||||||
|
icon="history"
|
||||||
|
@click.stop="openLineHistory(props.row)"
|
||||||
|
>
|
||||||
|
<q-tooltip>Eski Fiyatlar / Geçmiş</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
<q-icon
|
||||||
|
name="edit"
|
||||||
|
size="xs"
|
||||||
|
color="grey-7"
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click.stop="openRowEditorForEdit(props.row)"
|
||||||
|
>
|
||||||
|
<q-tooltip>Satırı Düzenle</q-tooltip>
|
||||||
|
</q-icon>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-maliyeteDahil="props">
|
||||||
|
<q-td :props="props" class="text-center pcd-secondary-cell">
|
||||||
|
<q-checkbox
|
||||||
|
v-model="props.row.maliyeteDahil"
|
||||||
|
color="primary"
|
||||||
|
keep-color
|
||||||
|
dense
|
||||||
|
@update:model-value="value => onRowMaliyeteDahilChange(props.row, value)"
|
||||||
|
/>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-cmPriceType="props">
|
||||||
|
<q-td :props="props" class="text-center pcd-secondary-cell">
|
||||||
|
<q-checkbox
|
||||||
|
v-if="isCMGroupName(props.row.sAciklama3)"
|
||||||
|
:model-value="resolveCMPriceTypeChecked(props.row)"
|
||||||
|
color="secondary"
|
||||||
|
keep-color
|
||||||
|
dense
|
||||||
|
@update:model-value="value => onRowCMPriceTypeChange(props.row, value)"
|
||||||
|
/>
|
||||||
|
<span v-else class="text-grey-6">-</span>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-nOnMLDetNo="props">
|
||||||
|
<q-td :props="props">
|
||||||
|
<div v-if="props.row.isNew" class="text-primary text-weight-bold">YENI</div>
|
||||||
|
<div>{{ props.value }}</div>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-sParcaAdi="props">
|
||||||
|
<q-td :props="props">
|
||||||
|
{{ props.value || props.row.sAciklama3 || '-' }}
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-nHammaddeTuruNo="props">
|
||||||
|
<q-td :props="props">
|
||||||
|
<div class="text-weight-medium" style="font-size: 1.1em;">
|
||||||
|
{{ props.value }}<span v-if="props.row.sHammaddeTuruAdi"> - {{ props.row.sHammaddeTuruAdi }}</span>
|
||||||
|
</div>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-sKodu="props">
|
||||||
|
<q-td :props="props">
|
||||||
|
<span>{{ props.value }}</span>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-sAciklama="props">
|
||||||
|
<q-td :props="props">
|
||||||
|
<span>{{ props.value }}</span>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-sRenk="props">
|
||||||
|
<q-td :props="props">
|
||||||
|
<span>{{ props.value }}</span>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-lMiktar="props">
|
||||||
|
<q-td :props="props">
|
||||||
|
<q-input
|
||||||
|
v-model="props.row.miktarInput"
|
||||||
|
class="pcd-inline-input pcd-entry-input"
|
||||||
|
color="primary"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
@update:model-value="value => onRowQuantityInput(props.row, value)"
|
||||||
|
@blur="normalizeRowQuantityDisplay(props.row)"
|
||||||
|
input-class="text-right"
|
||||||
|
inputmode="decimal"
|
||||||
|
placeholder="0,0000"
|
||||||
|
/>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-inputPrice="props">
|
||||||
|
<q-td :props="props">
|
||||||
|
<q-input
|
||||||
|
v-model="props.row.inputPrice"
|
||||||
|
class="pcd-inline-input pcd-entry-input"
|
||||||
|
color="primary"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
@update:model-value="value => onRowInputPriceChange(props.row, value)"
|
||||||
|
@blur="normalizeRowPriceDisplay(props.row)"
|
||||||
|
input-class="text-right"
|
||||||
|
inputmode="decimal"
|
||||||
|
placeholder="0,00"
|
||||||
|
/>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-inputPricePrBr="props">
|
||||||
|
<q-td :props="props">
|
||||||
|
<q-select
|
||||||
|
v-model="props.row.inputPricePrBr"
|
||||||
|
class="pcd-inline-input pcd-entry-input"
|
||||||
|
:options="priceCurrencyOptions"
|
||||||
|
color="primary"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
@update:model-value="value => onRowInputPriceCurrencyChange(props.row, value)"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
options-dense
|
||||||
|
/>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-sBirim="props">
|
||||||
|
<q-td :props="props">
|
||||||
|
<span>{{ props.value }}</span>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
</q-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-page>
|
||||||
|
|
||||||
|
<q-page v-else class="q-pa-md flex flex-center">
|
||||||
|
<div class="text-negative text-subtitle1">
|
||||||
|
Bu module erisim yetkiniz yok.
|
||||||
|
</div>
|
||||||
|
</q-page>
|
||||||
|
|
||||||
|
<q-dialog v-model="rowEditorDialogOpen" persistent>
|
||||||
|
<q-card class="pcd-row-editor-dialog">
|
||||||
|
<q-card-section class="row items-center justify-between q-pb-sm">
|
||||||
|
<div class="text-subtitle1 text-weight-bold">
|
||||||
|
{{ rowEditorMode === 'edit' ? 'Satir Duzenle' : 'Yeni Satir Ekle' }}
|
||||||
|
</div>
|
||||||
|
<q-btn flat round dense icon="close" v-close-popup />
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-separator />
|
||||||
|
|
||||||
|
<q-card-section class="q-pa-md">
|
||||||
|
<div class="row q-col-gutter-md">
|
||||||
|
<div class="col-12 col-md-2">
|
||||||
|
<q-input dense filled readonly label="No" v-model="rowEditorForm.nOnMLDetNo" />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-3">
|
||||||
|
<q-input dense filled readonly label="Parca Adi" v-model="rowEditorForm.sParcaAdi" />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-3">
|
||||||
|
<q-input dense filled readonly label="Parca Grubu" v-model="rowEditorForm.sAciklama3" />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-4">
|
||||||
|
<q-select
|
||||||
|
v-model="rowEditorForm.nHammaddeTuruNo"
|
||||||
|
class="pcd-row-editor-entry"
|
||||||
|
:options="rowEditorHammaddeOptions"
|
||||||
|
:loading="rowEditorHammaddeLoading"
|
||||||
|
option-value="value"
|
||||||
|
option-label="label"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
fill-input
|
||||||
|
hide-selected
|
||||||
|
input-debounce="250"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
label="Hammadde Turu"
|
||||||
|
placeholder="Aramak icin yazin"
|
||||||
|
@filter="filterRowEditorHammaddeOptions"
|
||||||
|
@update:model-value="onRowEditorHammaddeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<q-select
|
||||||
|
v-model="rowEditorForm.sKodu"
|
||||||
|
class="pcd-row-editor-entry"
|
||||||
|
:options="rowEditorItemOptions"
|
||||||
|
:loading="rowEditorItemLoading"
|
||||||
|
option-value="value"
|
||||||
|
option-label="label"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
fill-input
|
||||||
|
hide-selected
|
||||||
|
input-debounce="350"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
label="Kod / Aciklama"
|
||||||
|
placeholder="En az 2 karakter yazin"
|
||||||
|
@filter="filterRowEditorItemOptions"
|
||||||
|
@update:model-value="onRowEditorItemChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<q-select
|
||||||
|
v-model="rowEditorForm.ColorCode"
|
||||||
|
class="pcd-row-editor-entry"
|
||||||
|
:options="rowEditorColorOptions"
|
||||||
|
:loading="rowEditorColorLoading"
|
||||||
|
option-value="value"
|
||||||
|
option-label="label"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
fill-input
|
||||||
|
hide-selected
|
||||||
|
input-debounce="250"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
label="Renk"
|
||||||
|
placeholder="Aramak icin yazin"
|
||||||
|
@filter="filterRowEditorColorOptions"
|
||||||
|
@update:model-value="onRowEditorColorChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-2">
|
||||||
|
<q-input v-model="rowEditorForm.miktarInput" class="pcd-row-editor-entry" dense filled label="Miktar" input-class="text-right" inputmode="decimal" />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-2">
|
||||||
|
<q-input v-model="rowEditorForm.inputPrice" class="pcd-row-editor-entry" dense filled label="Fiyat Giris" input-class="text-right" inputmode="decimal" />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-2">
|
||||||
|
<q-select
|
||||||
|
v-model="rowEditorForm.inputPricePrBr"
|
||||||
|
class="pcd-row-editor-entry"
|
||||||
|
:options="priceCurrencyOptions"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
options-dense
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
label="Pr.Br."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-3">
|
||||||
|
<q-input dense filled readonly label="Birim" v-model="rowEditorForm.sBirim" />
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-2 pcd-row-editor-flag" style="display:flex;align-items:center;">
|
||||||
|
<q-checkbox v-model="rowEditorForm.maliyeteDahil" label="Maliyete Dahil" color="primary" keep-color />
|
||||||
|
</div>
|
||||||
|
<div v-if="isCMGroupName(rowEditorForm.sAciklama3)" class="col-12 col-md-3 pcd-row-editor-flag" style="display:flex;align-items:center;">
|
||||||
|
<q-checkbox v-model="rowEditorForm.cmPriceTypeChecked" label="CMT Malzeme Dahil" color="secondary" keep-color />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-card-actions align="right" class="q-pa-md">
|
||||||
|
<q-btn
|
||||||
|
v-if="rowEditorMode === 'edit'"
|
||||||
|
flat
|
||||||
|
color="negative"
|
||||||
|
icon="delete"
|
||||||
|
label="Satiri Sil"
|
||||||
|
class="q-mr-auto"
|
||||||
|
@click="deleteRowEditor"
|
||||||
|
/>
|
||||||
|
<q-btn flat label="Vazgec" color="grey-7" v-close-popup />
|
||||||
|
<q-btn color="secondary" icon="save" label="Satira Uygula" @click="saveRowEditor" />
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
|
<q-dialog v-model="lineHistoryDialogOpen" maximized>
|
||||||
|
<q-card class="pcd-history-dialog">
|
||||||
|
<q-card-section class="row items-center justify-between q-gutter-sm">
|
||||||
|
<div>
|
||||||
|
<div class="text-subtitle1 text-weight-bold">Eski Satinalma ve Recete Fiyatlari</div>
|
||||||
|
<div class="text-caption text-grey-7">
|
||||||
|
{{ lineHistoryTargetSummary }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<q-btn flat round dense icon="close" v-close-popup />
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-separator />
|
||||||
|
|
||||||
|
<q-card-section class="q-pa-md">
|
||||||
|
<q-banner v-if="lineHistoryError" class="bg-red text-white q-mb-md">
|
||||||
|
Hata: {{ lineHistoryError }}
|
||||||
|
</q-banner>
|
||||||
|
|
||||||
|
<q-banner v-if="lineHistoryShowingFallback" class="bg-amber-1 text-amber-9 q-mb-md rounded-borders border-amber">
|
||||||
|
<template v-slot:avatar>
|
||||||
|
<q-icon name="info" color="amber-9" />
|
||||||
|
</template>
|
||||||
|
Tam eşleşme bulunamadı. Benzer kodlara (prefix) ait geçmiş fiyatlar gösteriliyor.
|
||||||
|
</q-banner>
|
||||||
|
|
||||||
|
<div v-if="lineHistoryCanFetchSimilar || lineHistoryCanFetchAlternative" class="row q-col-gutter-sm q-mb-md">
|
||||||
|
<div v-if="lineHistorySearchMode !== 'exact'" class="col-auto">
|
||||||
|
<q-btn
|
||||||
|
label="Geri (Tam Eslesme)"
|
||||||
|
icon="arrow_back"
|
||||||
|
color="grey-7"
|
||||||
|
outline
|
||||||
|
no-caps
|
||||||
|
@click="fetchSimilarItemHistory('exact')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="lineHistoryCanFetchSimilar" class="col-auto">
|
||||||
|
<q-btn
|
||||||
|
label="Benzer Kodları Göster"
|
||||||
|
icon="travel_explore"
|
||||||
|
color="secondary"
|
||||||
|
outline
|
||||||
|
no-caps
|
||||||
|
:loading="lineHistoryLoading && lineHistorySearchMode === 'prefix'"
|
||||||
|
@click="fetchSimilarItemHistory('prefix')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div v-if="lineHistoryCanFetchAlternative" class="col-auto">
|
||||||
|
<q-btn
|
||||||
|
label="Diğer Alternatifleri Göster"
|
||||||
|
icon="alt_route"
|
||||||
|
color="primary"
|
||||||
|
outline
|
||||||
|
no-caps
|
||||||
|
:loading="lineHistoryLoading && lineHistorySearchMode === 'alternative'"
|
||||||
|
@click="fetchSimilarItemHistory('alternative')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="lineHistoryLoading" class="row justify-center q-pa-lg">
|
||||||
|
<q-spinner color="primary" size="32px" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="lineHistoryRows.length === 0" class="column items-center q-pa-xl">
|
||||||
|
<q-icon name="history" size="64px" color="grey-4" />
|
||||||
|
<div class="text-grey-7 q-mt-md text-center">
|
||||||
|
Bu kalem için geçmiş hareket bulunamadı.
|
||||||
|
<div v-if="lineHistoryTargetHammaddeTuruNo" class="q-mt-sm">
|
||||||
|
<q-btn
|
||||||
|
label="Hammadde Türü Bazında Benzer Ürünleri Göster"
|
||||||
|
icon="travel_explore"
|
||||||
|
color="secondary"
|
||||||
|
outline
|
||||||
|
no-caps
|
||||||
|
:loading="lineHistoryLoading"
|
||||||
|
@click="fetchSimilarItemHistory('prefix')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
|
<q-table
|
||||||
|
class="pcd-history-table"
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
bordered
|
||||||
|
row-key="__historyKey"
|
||||||
|
:rows="lineHistoryRows"
|
||||||
|
:columns="lineHistoryColumns"
|
||||||
|
:table-row-class-fn="resolveLineHistoryRowClass"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
hide-bottom
|
||||||
|
>
|
||||||
|
<template v-slot:body-cell-sourceLabel="props">
|
||||||
|
<q-td :props="props">
|
||||||
|
<span class="pcd-history-source-chip" :class="`pcd-history-source-chip--${String(props.row.sourceType || '').toLowerCase()}`">
|
||||||
|
{{ props.row.sourceLabel }}
|
||||||
|
</span>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:body-cell-price="props">
|
||||||
|
<q-td :props="props" class="text-right">
|
||||||
|
{{ formatMoney(props.row.price) }}
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:body-cell-quantity="props">
|
||||||
|
<q-td :props="props" class="text-right">
|
||||||
|
{{ formatMoney(props.row.quantity) }}
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:body-cell-amount="props">
|
||||||
|
<q-td :props="props" class="text-right">
|
||||||
|
{{ formatMoney(props.row.amount) }}
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:body-cell-select="props">
|
||||||
|
<q-td :props="props" class="text-right">
|
||||||
|
<q-btn
|
||||||
|
label="Sec"
|
||||||
|
color="primary"
|
||||||
|
dense
|
||||||
|
unelevated
|
||||||
|
@click="applyLineHistorySelection(props.row)"
|
||||||
|
/>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
</q-table>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||||
|
import { useQuasar } from 'quasar'
|
||||||
|
import { onBeforeRouteLeave, useRoute, useRouter } from 'vue-router'
|
||||||
|
import { usePermission } from 'src/composables/usePermission'
|
||||||
|
import { get, post, extractApiErrorDetail } from 'src/services/api'
|
||||||
|
import { createTraceId, slog } from 'src/utils/slog'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const $q = useQuasar()
|
||||||
|
const { canRead } = usePermission()
|
||||||
|
const canReadOrder = canRead('order')
|
||||||
|
|
||||||
|
const detailLoading = ref(false)
|
||||||
|
const detailError = ref('')
|
||||||
|
const detailGroups = ref([])
|
||||||
|
const detailHeader = ref(null)
|
||||||
|
const costDate = ref('')
|
||||||
|
const exchangeRates = ref(createEmptyExchangeRates())
|
||||||
|
const deletedDetailRows = ref([])
|
||||||
|
const localDraftTimer = ref(0)
|
||||||
|
const initialHeaderSnapshot = ref('')
|
||||||
|
const saveLoading = ref(false)
|
||||||
|
const bulkPriceLoading = ref(false)
|
||||||
|
const newRowSequence = ref(0)
|
||||||
|
const ROW_EDITOR_OPTIONS_LIMIT = 100
|
||||||
|
const ROW_EDITOR_ITEM_MIN_SEARCH_LENGTH = 2
|
||||||
|
const LINE_HISTORY_ROW_LIMIT = 500
|
||||||
|
const LINE_HISTORY_COMBINED_ROW_LIMIT = LINE_HISTORY_ROW_LIMIT * 2
|
||||||
|
const rowEditorDialogOpen = ref(false)
|
||||||
|
const rowEditorMode = ref('new')
|
||||||
|
const rowEditorTargetRowKey = ref('')
|
||||||
|
const rowEditorForm = ref(createRowEditorForm())
|
||||||
|
const rowEditorHammaddeOptions = ref([])
|
||||||
|
const rowEditorHammaddeAllOptions = ref([])
|
||||||
|
const rowEditorHammaddeLoading = ref(false)
|
||||||
|
const rowEditorItemOptions = ref([])
|
||||||
|
const rowEditorItemAllOptions = ref([])
|
||||||
|
const rowEditorItemLoading = ref(false)
|
||||||
|
const rowEditorColorOptions = ref([])
|
||||||
|
const rowEditorColorAllOptions = ref([])
|
||||||
|
const rowEditorColorLoading = ref(false)
|
||||||
|
const lineHistoryDialogOpen = ref(false)
|
||||||
|
const lineHistoryLoading = ref(false)
|
||||||
|
const lineHistoryError = ref('')
|
||||||
|
const lineHistoryRows = ref([])
|
||||||
|
const lineHistoryTargetRowKey = ref('')
|
||||||
|
const lineHistoryTargetHammaddeTuruNo = ref('')
|
||||||
|
const lineHistoryTargetItemCode = ref('')
|
||||||
|
const lineHistoryTargetSummary = ref('')
|
||||||
|
const lineHistorySearchMode = ref('exact')
|
||||||
|
const lineHistoryLastPurchaseMatchStage = ref('')
|
||||||
|
const lineHistoryLastRecipeMatchStage = ref('')
|
||||||
|
const headerInfoCollapsed = ref(false)
|
||||||
|
const subHeaderTop = ref(140)
|
||||||
|
const stickyStackRef = ref(null)
|
||||||
|
const saveToolbarRef = ref(null)
|
||||||
|
const groupOpenState = ref({})
|
||||||
|
const productionTypes = ref([])
|
||||||
|
|
||||||
|
const onMLNo = computed(() => String(route.query?.n_onml_no || '').trim())
|
||||||
|
const productCode = computed(() => String(route.query?.urun_kodu || '').trim())
|
||||||
|
const recipeCode = computed(() => String(route.query?.recete_kodu || '').trim())
|
||||||
|
const detailSource = computed(() => String(route.query?.detail_source || '').trim().toLowerCase())
|
||||||
|
const isNoCostDetail = computed(() => detailSource.value === 'no-cost')
|
||||||
|
const generatedTraceId = ref(createTraceId('pcd-detail'))
|
||||||
|
const traceId = computed(() => String(route.query?.trace_id || generatedTraceId.value).trim())
|
||||||
|
const pageMode = computed(() => (isNoCostDetail.value ? 'new' : 'edit'))
|
||||||
|
const pageVars = computed(() => ({
|
||||||
|
'--pcd-subheader-top': `${subHeaderTop.value}px`
|
||||||
|
}))
|
||||||
|
const lineHistoryCanFetchSimilar = computed(() => (
|
||||||
|
Boolean(lineHistoryTargetItemCode.value) &&
|
||||||
|
lineHistorySearchMode.value !== 'prefix'
|
||||||
|
))
|
||||||
|
const lineHistoryCanFetchAlternative = computed(() => (
|
||||||
|
Boolean(lineHistoryTargetHammaddeTuruNo.value) &&
|
||||||
|
lineHistorySearchMode.value !== 'alternative'
|
||||||
|
))
|
||||||
|
const lineHistoryShowingFallback = computed(() => (
|
||||||
|
lineHistorySearchMode.value === 'exact' &&
|
||||||
|
lineHistoryRows.value.some(r => r.priceType === 'BNZ')
|
||||||
|
))
|
||||||
|
const priceCurrencyOptions = [
|
||||||
|
{ label: 'USD', value: 'USD' },
|
||||||
|
{ label: 'TRY', value: 'TRY' },
|
||||||
|
{ label: 'EUR', value: 'EUR' },
|
||||||
|
{ label: 'GBP', value: 'GBP' }
|
||||||
|
]
|
||||||
|
const flatDetailRows = computed(() => detailGroups.value.flatMap(grp => Array.isArray(grp?.items) ? grp.items : []))
|
||||||
|
|
||||||
|
// no-cost: required parca slots (from Maliyet Parca Eslestirme)
|
||||||
|
const requiredParcaMappings = ref([])
|
||||||
|
const requiredAttentionRowKeys = ref({})
|
||||||
|
|
||||||
|
const draftStorageKey = computed(() => {
|
||||||
|
if (isNoCostDetail.value) {
|
||||||
|
if (!recipeCode.value) return ''
|
||||||
|
return `pcd-costing:no-cost:${String(productCode.value || '').trim()}:${String(recipeCode.value || '').trim()}`
|
||||||
|
}
|
||||||
|
if (!onMLNo.value) return ''
|
||||||
|
return `pcd-costing:has-cost:${String(onMLNo.value).trim()}`
|
||||||
|
})
|
||||||
|
|
||||||
|
const currentHeaderSnapshot = computed(() => JSON.stringify({
|
||||||
|
UretimSekliID: String(detailHeader.value?.UretimSekliID || '').trim(),
|
||||||
|
UretimSekli: String(detailHeader.value?.UretimSekli || '').trim()
|
||||||
|
}))
|
||||||
|
|
||||||
|
const headerHasUnsavedChanges = computed(() => {
|
||||||
|
if (!initialHeaderSnapshot.value) return false
|
||||||
|
return currentHeaderSnapshot.value !== initialHeaderSnapshot.value
|
||||||
|
})
|
||||||
|
|
||||||
|
const hasUnsavedChanges = computed(() => {
|
||||||
|
if (deletedDetailRows.value.length > 0) return true
|
||||||
|
if (headerHasUnsavedChanges.value) return true
|
||||||
|
return flatDetailRows.value.some(row => Boolean(row?.draftChanged))
|
||||||
|
})
|
||||||
|
|
||||||
|
function persistLocalDraftNow () {
|
||||||
|
const key = draftStorageKey.value
|
||||||
|
if (!key) return
|
||||||
|
try {
|
||||||
|
const payload = {
|
||||||
|
version: 1,
|
||||||
|
savedAt: new Date().toISOString(),
|
||||||
|
mode: pageMode.value,
|
||||||
|
detail_source: detailSource.value || 'has-cost',
|
||||||
|
n_onml_no: onMLNo.value,
|
||||||
|
urun_kodu: productCode.value,
|
||||||
|
recete_kodu: recipeCode.value,
|
||||||
|
header: detailHeader.value ? {
|
||||||
|
UretimSekliID: String(detailHeader.value?.UretimSekliID || '').trim(),
|
||||||
|
UretimSekli: String(detailHeader.value?.UretimSekli || '').trim()
|
||||||
|
} : null,
|
||||||
|
deletedDetailRows: Array.isArray(deletedDetailRows.value) ? deletedDetailRows.value : [],
|
||||||
|
detailGroups: Array.isArray(detailGroups.value) ? detailGroups.value : []
|
||||||
|
}
|
||||||
|
localStorage.setItem(key, JSON.stringify(payload))
|
||||||
|
} catch (err) {
|
||||||
|
slog.error('production-product-costing.detail', 'draft:persist:error', {
|
||||||
|
trace_id: traceId.value,
|
||||||
|
key: draftStorageKey.value,
|
||||||
|
error: String(err?.message || err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function schedulePersistLocalDraft () {
|
||||||
|
try {
|
||||||
|
if (localDraftTimer.value) window.clearTimeout(localDraftTimer.value)
|
||||||
|
localDraftTimer.value = window.setTimeout(() => {
|
||||||
|
localDraftTimer.value = 0
|
||||||
|
persistLocalDraftNow()
|
||||||
|
}, 400)
|
||||||
|
} catch {
|
||||||
|
persistLocalDraftNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearLocalDraft () {
|
||||||
|
const key = draftStorageKey.value
|
||||||
|
if (!key) return
|
||||||
|
try {
|
||||||
|
localStorage.removeItem(key)
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryHydrateFromLocalDraft () {
|
||||||
|
const key = draftStorageKey.value
|
||||||
|
if (!key) return false
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(key)
|
||||||
|
if (!raw) return false
|
||||||
|
const payload = JSON.parse(raw)
|
||||||
|
if (!payload || typeof payload !== 'object') return false
|
||||||
|
if (Array.isArray(payload.deletedDetailRows)) deletedDetailRows.value = payload.deletedDetailRows
|
||||||
|
if (Array.isArray(payload.detailGroups)) detailGroups.value = normalizeDetailGroups(payload.detailGroups)
|
||||||
|
if (payload.header && detailHeader.value) {
|
||||||
|
detailHeader.value.UretimSekliID = String(payload.header.UretimSekliID || '').trim()
|
||||||
|
detailHeader.value.UretimSekli = String(payload.header.UretimSekli || '').trim()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureBeforeUnloadGuard (enabled) {
|
||||||
|
if (!enabled) {
|
||||||
|
window.onbeforeunload = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
window.onbeforeunload = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.returnValue = ''
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const toolbarSummary = computed(() => flatDetailRows.value.reduce((acc, row) => {
|
||||||
|
if (!row?.maliyeteDahil) return acc
|
||||||
|
acc.tryTotal += resolveRowTRYTutar(row)
|
||||||
|
acc.usdTotal += resolveRowUSDTutar(row)
|
||||||
|
acc.eurTotal += resolveRowEURTutar(row)
|
||||||
|
acc.gbpTotal += resolveRowGBPTutar(row)
|
||||||
|
return acc
|
||||||
|
}, {
|
||||||
|
tryTotal: 0,
|
||||||
|
usdTotal: 0,
|
||||||
|
eurTotal: 0,
|
||||||
|
gbpTotal: 0
|
||||||
|
}))
|
||||||
|
|
||||||
|
const partSummary = computed(() => {
|
||||||
|
const summary = {}
|
||||||
|
flatDetailRows.value.forEach(row => {
|
||||||
|
if (!row?.maliyeteDahil) return
|
||||||
|
const part = String(row?.sParcaAdi || 'TANIMSIZ').trim() || 'TANIMSIZ'
|
||||||
|
if (!summary[part]) {
|
||||||
|
summary[part] = { try: 0, usd: 0, eur: 0, gbp: 0 }
|
||||||
|
}
|
||||||
|
summary[part].try += resolveRowTRYTutar(row)
|
||||||
|
summary[part].usd += resolveRowUSDTutar(row)
|
||||||
|
summary[part].eur += resolveRowEURTutar(row)
|
||||||
|
summary[part].gbp += resolveRowGBPTutar(row)
|
||||||
|
})
|
||||||
|
return Object.entries(summary).map(([name, totals]) => ({ name, ...totals }))
|
||||||
|
})
|
||||||
|
const lineHistoryColumns = [
|
||||||
|
{ name: 'sourceLabel', label: 'Kaynak', field: 'sourceLabel', align: 'left', sortable: false, style: 'width:6%', headerStyle: 'width:6%' },
|
||||||
|
{ name: 'dateLabel', label: 'Tarih', field: 'dateLabel', align: 'left', sortable: true, style: 'width:7%', headerStyle: 'width:7%' },
|
||||||
|
{ name: 'invoiceCode', label: 'Fatura/OnML', field: 'invoiceCode', align: 'left', sortable: true, style: 'width:8%', headerStyle: 'width:8%' },
|
||||||
|
{ name: 'companyCode', label: 'Firma Kodu', field: 'companyCode', align: 'left', sortable: true, style: 'width:8%', headerStyle: 'width:8%' },
|
||||||
|
{ name: 'companyDescription', label: 'Firma Aciklama', field: 'companyDescription', align: 'left', sortable: true, style: 'width:12%', headerStyle: 'width:12%' },
|
||||||
|
{ name: 'itemCode', label: 'Masraf/sKodu', field: 'itemCode', align: 'left', sortable: true, style: 'width:8%', headerStyle: 'width:8%' },
|
||||||
|
{ name: 'itemDescription', label: 'Masraf Detay', field: 'itemDescription', align: 'left', sortable: true, style: 'width:11%', headerStyle: 'width:11%' },
|
||||||
|
{ name: 'colorCode', label: 'Renk', field: 'colorCode', align: 'left', sortable: true, style: 'width:5%', headerStyle: 'width:5%' },
|
||||||
|
{ name: 'colorDescription', label: 'Renk Aciklama', field: 'colorDescription', align: 'left', sortable: true, style: 'width:8%', headerStyle: 'width:8%' },
|
||||||
|
{ name: 'itemDim1Code', label: 'Dim1', field: 'itemDim1Code', align: 'left', sortable: true, style: 'width:5%', headerStyle: 'width:5%' },
|
||||||
|
{ name: 'itemDim1Description', label: 'Dim1 Aciklama', field: 'itemDim1Description', align: 'left', sortable: true, style: 'width:8%', headerStyle: 'width:8%' },
|
||||||
|
{ name: 'quantity', label: 'Miktar', field: 'quantity', align: 'right', sortable: true, style: 'width:6%', headerStyle: 'width:6%' },
|
||||||
|
{ name: 'unit', label: 'Birim', field: 'unit', align: 'left', sortable: true, style: 'width:4%', headerStyle: 'width:4%' },
|
||||||
|
{ name: 'price', label: 'Fiyat', field: 'price', align: 'right', sortable: true, style: 'width:6%', headerStyle: 'width:6%' },
|
||||||
|
{ name: 'amount', label: 'Tutar', field: 'amount', align: 'right', sortable: true, style: 'width:6%', headerStyle: 'width:6%' },
|
||||||
|
{ name: 'currency', label: 'Pr. Br.', field: 'currency', align: 'left', sortable: true, style: 'width:5%', headerStyle: 'width:5%' },
|
||||||
|
{ name: 'select', label: '', field: 'select', align: 'right', sortable: false, style: 'width:6%', headerStyle: 'width:6%' }
|
||||||
|
]
|
||||||
|
|
||||||
|
function resolveLineHistoryRowClass (row) {
|
||||||
|
if (row?.priceType === 'BNZ') return 'pcd-history-row-similar'
|
||||||
|
if (row?.sourceType === 'recipe') return 'pcd-history-row-recipe'
|
||||||
|
if (row?.sourceType === 'purchase') return 'pcd-history-row-purchase'
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const detailColumns = [
|
||||||
|
{ name: 'actions', label: '', field: 'actions', align: 'left', style: 'width:60px', headerStyle: 'width:60px' },
|
||||||
|
{ name: 'nOnMLDetNo', label: 'No', field: 'nOnMLDetNo', align: 'left', sortable: true },
|
||||||
|
{ name: 'sParcaAdi', label: 'Parça Adı', field: 'sParcaAdi', align: 'left', sortable: true },
|
||||||
|
{ name: 'nHammaddeTuruNo', label: 'Hammadde Türü', field: 'nHammaddeTuruNo', align: 'left', sortable: true },
|
||||||
|
{ name: 'sKodu', label: 'Kod', field: 'sKodu', align: 'left', sortable: true },
|
||||||
|
{ name: 'sAciklama', label: 'Açıklama', field: 'sAciklama', align: 'left', sortable: true },
|
||||||
|
{ name: 'sRenk', label: 'Renk', field: 'sRenk', align: 'left', sortable: true },
|
||||||
|
{ name: 'lMiktar', label: 'Miktar', field: 'lMiktar', align: 'right', sortable: true, format: val => formatQuantity(val), style: 'width: 80px', headerStyle: 'width: 80px' },
|
||||||
|
{ name: 'inputPrice', label: 'Fiyat Giriş', field: 'inputPrice', align: 'right', sortable: false, style: 'width: 80px', headerStyle: 'width: 80px' },
|
||||||
|
{ name: 'inputPricePrBr', label: 'Fiyat Giriş Pr.Br.', field: 'inputPricePrBr', align: 'left', sortable: false, style: 'width: 80px', headerStyle: 'width: 80px' },
|
||||||
|
{ name: 'maliyeteDahil', label: 'Maliyete Dahil', field: 'maliyeteDahil', align: 'center', sortable: false },
|
||||||
|
{ name: 'cmPriceType', label: 'CMT', field: 'cm_price_type_id', align: 'center', sortable: false, style: 'width: 72px', headerStyle: 'width: 72px' },
|
||||||
|
{ name: 'lFiyat', label: 'lFiyat', field: 'lFiyat', align: 'right', sortable: true, format: val => formatMoney(val) },
|
||||||
|
{ name: 'lTutar', label: 'lTutar', field: 'lTutar', align: 'right', sortable: true, format: val => formatMoney(val) },
|
||||||
|
{ name: 'sFiyatTipi', label: 'sFiyatTipi', field: 'sFiyatTipi', align: 'left', sortable: true },
|
||||||
|
{ name: 'sDovizCinsi', label: 'Döviz', field: 'sDovizCinsi', align: 'left', sortable: true },
|
||||||
|
{ name: 'lDovizKuru', label: 'Kur', field: 'lDovizKuru', align: 'right', sortable: true, format: val => formatMoney(val) },
|
||||||
|
{ name: 'lDovizFiyati', label: 'Döviz Fiyatı', field: 'lDovizFiyati', align: 'right', sortable: true, format: val => formatMoney(val) },
|
||||||
|
{ name: 'priceUpdateState', label: 'Fiyat Tipi', field: 'priceUpdateState', align: 'left', sortable: true },
|
||||||
|
{
|
||||||
|
name: 'usdTutar',
|
||||||
|
label: 'USD TUTAR',
|
||||||
|
field: row => resolveRowUSDTutar(row),
|
||||||
|
align: 'right',
|
||||||
|
sortable: true,
|
||||||
|
format: val => formatMoney(val)
|
||||||
|
},
|
||||||
|
{ name: 'sBirim', label: 'Birim', field: 'sBirim', align: 'left', sortable: true }
|
||||||
|
]
|
||||||
|
|
||||||
|
function formatDateTR (value) {
|
||||||
|
const s = String(value || '').trim()
|
||||||
|
if (!s) return ''
|
||||||
|
const m = /^(\d{4})-(\d{2})-(\d{2})(?:\s+(\d{2}):(\d{2}))?/.exec(s)
|
||||||
|
if (!m) return s
|
||||||
|
const datePart = `${m[3]}.${m[2]}.${m[1]}`
|
||||||
|
if (m[4] && m[5]) return `${datePart} ${m[4]}:${m[5]}`
|
||||||
|
return datePart
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatUretimSekli (header) {
|
||||||
|
const id = String(header?.UretimSekliID || '').trim()
|
||||||
|
const aciklama = String(header?.UretimSekli || '').trim()
|
||||||
|
|
||||||
|
if (id && aciklama) return `${id}-${aciklama}`
|
||||||
|
return id || aciklama || '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeDateInput (value) {
|
||||||
|
const s = String(value || '').trim()
|
||||||
|
const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(s)
|
||||||
|
if (!m) return ''
|
||||||
|
return `${m[1]}-${m[2]}-${m[3]}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCMGroupName (value) {
|
||||||
|
const normalizedValue = String(value || '').trim().toUpperCase()
|
||||||
|
return normalizedValue.includes('CM1') || normalizedValue.includes('CM2')
|
||||||
|
}
|
||||||
|
|
||||||
|
function createEmptyExchangeRates () {
|
||||||
|
return {
|
||||||
|
rateDate: '',
|
||||||
|
tryRate: 1,
|
||||||
|
usdRate: 0,
|
||||||
|
eurRate: 0,
|
||||||
|
gbpRate: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRowEditorForm (seed = {}) {
|
||||||
|
const defaultCurrency = normalizePriceCurrency(seed?.inputPricePrBr || seed?.fiyat_doviz || detailHeader.value?.sDovizCinsi) || 'USD'
|
||||||
|
const cmPriceTypeId = normalizeCMPriceTypeId(seed?.cmPriceTypeId ?? seed?.cm_price_type_id, seed?.sAciklama3 ?? seed?.sParcaAdi)
|
||||||
|
return {
|
||||||
|
__rowKey: String(seed?.__rowKey || '').trim(),
|
||||||
|
isNew: Boolean(seed?.isNew),
|
||||||
|
nStokID: String(seed?.nStokID || '').trim(),
|
||||||
|
sModel: String(seed?.sModel || '').trim(),
|
||||||
|
nOnMLDetNo: String(seed?.nOnMLDetNo || '').trim(),
|
||||||
|
sParcaAdi: String(seed?.sParcaAdi || seed?.sAciklama3 || '').trim(),
|
||||||
|
nHammaddeTuruNo: String(seed?.nHammaddeTuruNo || '').trim(),
|
||||||
|
sHammaddeTuruAdi: String(seed?.sHammaddeTuruAdi || '').trim(),
|
||||||
|
sAciklama3: String(seed?.sAciklama3 || seed?.sParcaAdi || '').trim(),
|
||||||
|
sKodu: String(seed?.sKodu || '').trim(),
|
||||||
|
sAciklama: String(seed?.sAciklama || '').trim(),
|
||||||
|
sRenk: String(seed?.sRenk || seed?.ColorCode || '').trim(),
|
||||||
|
ColorCode: String(seed?.ColorCode || seed?.sRenk || '').trim(),
|
||||||
|
ColorDescription: String(seed?.ColorDescription || '').trim(),
|
||||||
|
miktarInput: normalizeQuantityInput(seed?.miktarInput ?? seed?.lMiktar ?? 1),
|
||||||
|
inputPrice: normalizeInputPrice(seed?.inputPrice ?? seed?.fiyat_girilen ?? 0),
|
||||||
|
inputPricePrBr: defaultCurrency,
|
||||||
|
maliyeteDahil: seed?.maliyeteDahil ?? normalizeBooleanFlag(seed?.maliyete_dahil ?? true),
|
||||||
|
cmPriceTypeChecked: cmPriceTypeId === 2,
|
||||||
|
sBirim: extractPrimaryUnitValue(seed?.sBirim || 'AD') || 'AD'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatMoney (value) {
|
||||||
|
return parseMoneyInput(value).toLocaleString('tr-TR', {
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatQuantity (value) {
|
||||||
|
return parseMoneyInput(value).toLocaleString('tr-TR', {
|
||||||
|
minimumFractionDigits: 4,
|
||||||
|
maximumFractionDigits: 4
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatBarMoney (value) {
|
||||||
|
const roundedValue = Math.round((Number(value || 0) + Number.EPSILON) * 100) / 100
|
||||||
|
return formatMoney(roundedValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizePriceCurrency (value) {
|
||||||
|
const normalizedValue = String(value || '').trim().toUpperCase()
|
||||||
|
return ['USD', 'TRY', 'EUR', 'GBP'].includes(normalizedValue) ? normalizedValue : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeCodeValue (value) {
|
||||||
|
return String(value ?? '').trim().toUpperCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractPrimaryUnitValue (value) {
|
||||||
|
const normalizedValue = String(value || '').trim()
|
||||||
|
if (!normalizedValue) return ''
|
||||||
|
return normalizedValue.split(/[\s,;|/]+/).filter(Boolean)[0] || normalizedValue
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeInputPrice (value) {
|
||||||
|
const numericValue = parseMoneyInput(value)
|
||||||
|
if (value === null || value === undefined || value === '' || !Number.isFinite(numericValue)) {
|
||||||
|
return formatMoney(0)
|
||||||
|
}
|
||||||
|
return formatMoney(numericValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeQuantityInput (value) {
|
||||||
|
const numericValue = parseMoneyInput(value)
|
||||||
|
if (value === null || value === undefined || value === '' || !Number.isFinite(numericValue)) {
|
||||||
|
return formatQuantity(0)
|
||||||
|
}
|
||||||
|
return formatQuantity(numericValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeSingleSeparatorNumber (rawValue, separator) {
|
||||||
|
const parts = String(rawValue || '').split(separator)
|
||||||
|
if (parts.length <= 1) return String(rawValue || '')
|
||||||
|
if (parts.length === 2) {
|
||||||
|
return separator === ',' ? `${parts[0]}.${parts[1]}` : `${parts[0]}.${parts[1]}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const headParts = parts.slice(0, -1)
|
||||||
|
const lastPart = parts[parts.length - 1]
|
||||||
|
const looksLikeGroupedInteger = lastPart.length === 3 && headParts.every((part, index) => {
|
||||||
|
if (index === 0) return part.length >= 1 && part.length <= 3
|
||||||
|
return part.length === 3
|
||||||
|
})
|
||||||
|
|
||||||
|
if (looksLikeGroupedInteger) {
|
||||||
|
return parts.join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${headParts.join('')}.${lastPart}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseMoneyInput (value) {
|
||||||
|
const rawValue = String(value ?? '').trim()
|
||||||
|
if (!rawValue) return 0
|
||||||
|
|
||||||
|
const compactValue = rawValue.replace(/\s+/g, '')
|
||||||
|
const lastDotIndex = compactValue.lastIndexOf('.')
|
||||||
|
const lastCommaIndex = compactValue.lastIndexOf(',')
|
||||||
|
const hasDot = lastDotIndex >= 0
|
||||||
|
const hasComma = lastCommaIndex >= 0
|
||||||
|
|
||||||
|
let normalizedValue = compactValue
|
||||||
|
|
||||||
|
if (hasDot && hasComma) {
|
||||||
|
const decimalSeparator = lastDotIndex > lastCommaIndex ? '.' : ','
|
||||||
|
const thousandSeparator = decimalSeparator === '.' ? ',' : '.'
|
||||||
|
normalizedValue = compactValue.replace(new RegExp(`\\${thousandSeparator}`, 'g'), '')
|
||||||
|
if (decimalSeparator === ',') {
|
||||||
|
normalizedValue = normalizedValue.replace(',', '.')
|
||||||
|
}
|
||||||
|
} else if (hasComma) {
|
||||||
|
normalizedValue = normalizeSingleSeparatorNumber(compactValue, ',')
|
||||||
|
} else if (hasDot) {
|
||||||
|
normalizedValue = normalizeSingleSeparatorNumber(compactValue, '.')
|
||||||
|
}
|
||||||
|
|
||||||
|
const numericValue = Number(normalizedValue)
|
||||||
|
return Number.isFinite(numericValue) ? numericValue : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeBooleanFlag (value) {
|
||||||
|
if (typeof value === 'boolean') return value
|
||||||
|
if (typeof value === 'number') return value === 1
|
||||||
|
|
||||||
|
const normalizedValue = String(value ?? '').trim().toLowerCase()
|
||||||
|
return normalizedValue === '1' || normalizedValue === 'true' || normalizedValue === 'evet'
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeCMPriceTypeId (value, groupName) {
|
||||||
|
if (!isCMGroupName(groupName)) return null
|
||||||
|
if (value === null || value === undefined || value === '') return 1
|
||||||
|
|
||||||
|
const numericValue = Number(value)
|
||||||
|
if (numericValue === 2) return 2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveCMPriceTypeChecked (row) {
|
||||||
|
return normalizeCMPriceTypeId(row?.cmPriceTypeId ?? row?.cm_price_type_id, row?.sAciklama3) === 2
|
||||||
|
}
|
||||||
|
|
||||||
|
function firstDefinedValue (source, keys) {
|
||||||
|
for (const key of keys) {
|
||||||
|
const value = source?.[key]
|
||||||
|
if (value !== undefined && value !== null && String(value).trim() !== '') {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeExchangeRatesPayload (payload, fallbackDate = '') {
|
||||||
|
return {
|
||||||
|
rateDate: normalizeDateInput(firstDefinedValue(payload, ['rateDate', 'RateDate', 'date', 'Date'])) || fallbackDate,
|
||||||
|
tryRate: 1,
|
||||||
|
usdRate: parseMoneyInput(firstDefinedValue(payload, ['usdRate', 'USDRate', 'usd', 'USD'])),
|
||||||
|
eurRate: parseMoneyInput(firstDefinedValue(payload, ['eurRate', 'EURRate', 'eur', 'EUR'])),
|
||||||
|
gbpRate: parseMoneyInput(firstDefinedValue(payload, ['gbpRate', 'GBPRate', 'gbp', 'GBP']))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRowEditorHammaddeOption (source) {
|
||||||
|
const value = String(source?.value || source?.nHammaddeTuruNo || '').trim()
|
||||||
|
const name = String(source?.sHammaddeTuruAdi || '').trim()
|
||||||
|
const groupName = String(source?.sAciklama3 || '').trim()
|
||||||
|
return {
|
||||||
|
kind: 'hammadde',
|
||||||
|
value,
|
||||||
|
label: String(source?.label || `${value}${name ? ` - ${name}` : ''}`).trim(),
|
||||||
|
nHammaddeTuruNo: value,
|
||||||
|
sHammaddeTuruAdi: name,
|
||||||
|
sAciklama3: groupName,
|
||||||
|
sParcaAdi: String(source?.sParcaAdi || groupName).trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRowEditorItemOption (source) {
|
||||||
|
const value = String(source?.value || source?.sKodu || '').trim()
|
||||||
|
const desc = String(source?.sAciklama || '').trim()
|
||||||
|
return {
|
||||||
|
kind: 'item',
|
||||||
|
value,
|
||||||
|
nStokID: String(source?.nStokID || '').trim(),
|
||||||
|
sModel: String(source?.sModel || '').trim(),
|
||||||
|
label: String(source?.label || `${value}${desc ? ` - ${desc}` : ''}`).trim(),
|
||||||
|
sKodu: value,
|
||||||
|
sAciklama: desc,
|
||||||
|
sBirim: extractPrimaryUnitValue(source?.sBirim)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRowEditorColorOption (source) {
|
||||||
|
const value = String(source?.value || source?.colorCode || source?.ColorCode || source?.sRenk || '').trim()
|
||||||
|
const desc = String(source?.colorDescription || source?.ColorDescription || '').trim()
|
||||||
|
return {
|
||||||
|
kind: 'color',
|
||||||
|
value,
|
||||||
|
label: String(source?.label || `${value}${desc ? ` - ${desc}` : ''}`).trim(),
|
||||||
|
colorCode: value,
|
||||||
|
colorDescription: desc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function upsertEditorOption (optionsRef, option) {
|
||||||
|
if (!option?.value) return
|
||||||
|
const nextOptions = [...optionsRef.value]
|
||||||
|
const idx = nextOptions.findIndex(x => String(x?.value || '').trim() === option.value)
|
||||||
|
if (idx >= 0) {
|
||||||
|
nextOptions[idx] = { ...nextOptions[idx], ...option }
|
||||||
|
} else {
|
||||||
|
nextOptions.unshift(option)
|
||||||
|
}
|
||||||
|
optionsRef.value = nextOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchRowEditorOptions (kind, search = '', extraParams = {}) {
|
||||||
|
const response = await get('/pricing/production-product-costing/detail-editor-options', {
|
||||||
|
kind,
|
||||||
|
search,
|
||||||
|
limit: ROW_EDITOR_OPTIONS_LIMIT,
|
||||||
|
trace_id: traceId.value,
|
||||||
|
...extraParams
|
||||||
|
})
|
||||||
|
return Array.isArray(response) ? response : []
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRowEditorOptionsByKind (kind, rows) {
|
||||||
|
if (kind === 'hammadde') {
|
||||||
|
rowEditorHammaddeAllOptions.value = rows
|
||||||
|
rowEditorHammaddeOptions.value = rows
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (kind === 'item') {
|
||||||
|
rowEditorItemAllOptions.value = rows
|
||||||
|
rowEditorItemOptions.value = rows
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rowEditorColorAllOptions.value = rows
|
||||||
|
rowEditorColorOptions.value = rows
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRowEditorLookupLoading (kind, isLoading) {
|
||||||
|
if (kind === 'hammadde') {
|
||||||
|
rowEditorHammaddeLoading.value = isLoading
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (kind === 'item') {
|
||||||
|
rowEditorItemLoading.value = isLoading
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rowEditorColorLoading.value = isLoading
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshRowEditorOptions (kind, search = '') {
|
||||||
|
const extraParams = kind === 'color'
|
||||||
|
? { model_code: String(rowEditorForm.value.sModel || '').trim() }
|
||||||
|
: {}
|
||||||
|
|
||||||
|
if (kind === 'color' && !extraParams.model_code) {
|
||||||
|
setRowEditorOptionsByKind(kind, [])
|
||||||
|
primeRowEditorOptionsFromForm()
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
setRowEditorLookupLoading(kind, true)
|
||||||
|
try {
|
||||||
|
const rows = await fetchRowEditorOptions(kind, search, extraParams)
|
||||||
|
setRowEditorOptionsByKind(kind, rows)
|
||||||
|
primeRowEditorOptionsFromForm()
|
||||||
|
return rows
|
||||||
|
} finally {
|
||||||
|
setRowEditorLookupLoading(kind, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function filterRowEditorOptions (kind, val, update, abort) {
|
||||||
|
const normalizedSearch = String(val || '').trim()
|
||||||
|
if (kind === 'item' && normalizedSearch.length < ROW_EDITOR_ITEM_MIN_SEARCH_LENGTH) {
|
||||||
|
update(() => {
|
||||||
|
rowEditorItemAllOptions.value = []
|
||||||
|
rowEditorItemOptions.value = []
|
||||||
|
primeRowEditorOptionsFromForm()
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const rows = await refreshRowEditorOptions(kind, normalizedSearch)
|
||||||
|
update(() => {
|
||||||
|
setRowEditorOptionsByKind(kind, rows)
|
||||||
|
primeRowEditorOptionsFromForm()
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
abort()
|
||||||
|
$q.notify({
|
||||||
|
type: 'negative',
|
||||||
|
message: `Lookup getirilemedi: ${await extractApiErrorDetail(err)}`,
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterRowEditorHammaddeOptions (val, update, abort) {
|
||||||
|
return filterRowEditorOptions('hammadde', val, update, abort)
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterRowEditorItemOptions (val, update, abort) {
|
||||||
|
return filterRowEditorOptions('item', val, update, abort)
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterRowEditorColorOptions (val, update, abort) {
|
||||||
|
const modelCode = String(rowEditorForm.value.sModel || '').trim()
|
||||||
|
if (!modelCode) {
|
||||||
|
update(() => {
|
||||||
|
rowEditorColorAllOptions.value = []
|
||||||
|
rowEditorColorOptions.value = []
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return filterRowEditorOptions('color', val, update, abort)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadRowEditorColorOptions () {
|
||||||
|
const modelCode = String(rowEditorForm.value.sModel || '').trim()
|
||||||
|
if (!modelCode) return []
|
||||||
|
try {
|
||||||
|
return await fetchRowEditorOptions('color', '', { model_code: modelCode })
|
||||||
|
} catch (err) {
|
||||||
|
$q.notify({
|
||||||
|
type: 'negative',
|
||||||
|
message: `Renk lookup getirilemedi: ${await extractApiErrorDetail(err)}`,
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function primeRowEditorOptionsFromForm () {
|
||||||
|
upsertEditorOption(rowEditorHammaddeAllOptions, buildRowEditorHammaddeOption(rowEditorForm.value))
|
||||||
|
upsertEditorOption(rowEditorHammaddeOptions, buildRowEditorHammaddeOption(rowEditorForm.value))
|
||||||
|
upsertEditorOption(rowEditorItemAllOptions, buildRowEditorItemOption(rowEditorForm.value))
|
||||||
|
upsertEditorOption(rowEditorItemOptions, buildRowEditorItemOption(rowEditorForm.value))
|
||||||
|
upsertEditorOption(rowEditorColorAllOptions, buildRowEditorColorOption(rowEditorForm.value))
|
||||||
|
upsertEditorOption(rowEditorColorOptions, buildRowEditorColorOption(rowEditorForm.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNextDetailNo () {
|
||||||
|
const maxNo = flatDetailRows.value.reduce((acc, row) => {
|
||||||
|
const rowNo = parseInt(String(row?.nOnMLDetNo || '').trim(), 10)
|
||||||
|
return Number.isFinite(rowNo) ? Math.max(acc, rowNo) : acc
|
||||||
|
}, 0)
|
||||||
|
return String(maxNo + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveRowColorCode (row) {
|
||||||
|
return normalizeCodeValue(firstDefinedValue(row, [
|
||||||
|
'ColorCode',
|
||||||
|
'colorCode',
|
||||||
|
'sRenk',
|
||||||
|
's_renk',
|
||||||
|
'renk'
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveRowItemDim1Code (row) {
|
||||||
|
return normalizeCodeValue(firstDefinedValue(row, [
|
||||||
|
'ItemDim1Code',
|
||||||
|
'itemDim1Code',
|
||||||
|
'sBeden',
|
||||||
|
's_beden'
|
||||||
|
]))
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractNamedArray (payload, keys) {
|
||||||
|
for (const key of keys) {
|
||||||
|
if (Array.isArray(payload?.[key])) return payload[key]
|
||||||
|
if (Array.isArray(payload?.data?.[key])) return payload.data[key]
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseDateSortValue (value) {
|
||||||
|
const rawValue = String(value || '').trim()
|
||||||
|
if (!rawValue) return 0
|
||||||
|
const normalizedValue = rawValue.replace(' ', 'T')
|
||||||
|
const timeValue = Date.parse(normalizedValue)
|
||||||
|
return Number.isFinite(timeValue) ? timeValue : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortHistoryRowsByDateDesc (rows) {
|
||||||
|
return [...rows].sort((a, b) => parseDateSortValue(b?.dateValue) - parseDateSortValue(a?.dateValue))
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveHistorySourceType (item, forcedType = '') {
|
||||||
|
const normalizedType = String(forcedType || firstDefinedValue(item, [
|
||||||
|
'sourceType',
|
||||||
|
'source',
|
||||||
|
'kaynak',
|
||||||
|
'historyType',
|
||||||
|
'tip'
|
||||||
|
])).trim().toLowerCase()
|
||||||
|
|
||||||
|
if (['recipe', 'recete', 'uretim'].includes(normalizedType)) return 'recipe'
|
||||||
|
if (['purchase', 'invoice', 'satinalma', 'baggi_v3', 'baggi-v3'].includes(normalizedType)) return 'purchase'
|
||||||
|
if (firstDefinedValue(item, ['MasrafKodu', 'FaturaKodu', 'EvrakFiyat', 'ItemDim1Code']) !== '') return 'purchase'
|
||||||
|
if (firstDefinedValue(item, ['nOnMLNo', 'dteIslemTarihi', 'lDovizFiyati', 'lDovizTutari']) !== '') return 'recipe'
|
||||||
|
return normalizedType || 'history'
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveHistorySourceLabel (sourceType) {
|
||||||
|
if (sourceType === 'purchase') return 'BAGGI_V3'
|
||||||
|
if (sourceType === 'recipe') return 'URETIM'
|
||||||
|
return 'HISTORY'
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeHistoryRowFromItem (item, index, forcedType = '') {
|
||||||
|
const sourceType = resolveHistorySourceType(item, forcedType)
|
||||||
|
const price = parseMoneyInput(firstDefinedValue(item, [
|
||||||
|
'EvrakFiyat',
|
||||||
|
'evrakFiyat',
|
||||||
|
'fiyat_girilen',
|
||||||
|
'fiyatGirilen',
|
||||||
|
'price',
|
||||||
|
'fiyat',
|
||||||
|
'birimFiyat',
|
||||||
|
'unitPrice',
|
||||||
|
'lDovizFiyati',
|
||||||
|
'lFiyat'
|
||||||
|
]))
|
||||||
|
const quantity = parseMoneyInput(firstDefinedValue(item, [
|
||||||
|
'Miktar',
|
||||||
|
'miktar',
|
||||||
|
'quantity',
|
||||||
|
'qty',
|
||||||
|
'adet',
|
||||||
|
'lMiktar'
|
||||||
|
]))
|
||||||
|
const amountValue = firstDefinedValue(item, [
|
||||||
|
'EvrakTutar',
|
||||||
|
'evrakTutar',
|
||||||
|
'tutar',
|
||||||
|
'amount',
|
||||||
|
'toplamTutar',
|
||||||
|
'lTutar',
|
||||||
|
'lDovizTutari'
|
||||||
|
])
|
||||||
|
const currency = normalizePriceCurrency(firstDefinedValue(item, [
|
||||||
|
'EvrakDoviz',
|
||||||
|
'evrakDoviz',
|
||||||
|
'fiyat_doviz',
|
||||||
|
'fiyatDoviz',
|
||||||
|
'currency',
|
||||||
|
'priceCurrency',
|
||||||
|
'prBr',
|
||||||
|
'pb',
|
||||||
|
'sDovizCinsi',
|
||||||
|
'USD'
|
||||||
|
]))
|
||||||
|
const dateValue = String(firstDefinedValue(item, [
|
||||||
|
'Tarih',
|
||||||
|
'tarih',
|
||||||
|
'InvoiceDate',
|
||||||
|
'invoiceDate',
|
||||||
|
'dteIslemTarihi',
|
||||||
|
'islemTarihi',
|
||||||
|
'dteKayitTarihi',
|
||||||
|
'date'
|
||||||
|
])).trim()
|
||||||
|
const amount = amountValue === ''
|
||||||
|
? price * quantity
|
||||||
|
: parseMoneyInput(amountValue)
|
||||||
|
|
||||||
|
return {
|
||||||
|
__historyKey: String(firstDefinedValue(item, ['id', '__historyKey', 'historyKey'])).trim() || `${sourceType}-${index}`,
|
||||||
|
raw: item,
|
||||||
|
sourceType,
|
||||||
|
sourceLabel: resolveHistorySourceLabel(sourceType),
|
||||||
|
dateValue,
|
||||||
|
dateLabel: formatDateTR(dateValue),
|
||||||
|
invoiceCode: String(firstDefinedValue(item, [
|
||||||
|
'FaturaKodu',
|
||||||
|
'faturaKodu',
|
||||||
|
'InvoiceNumber',
|
||||||
|
'invoiceCode',
|
||||||
|
'FaturaNo',
|
||||||
|
'nOnMLNo',
|
||||||
|
'n_onml_no'
|
||||||
|
])).trim(),
|
||||||
|
companyCode: String(firstDefinedValue(item, [
|
||||||
|
'FirmaKodu',
|
||||||
|
'firmaKodu',
|
||||||
|
'CurrAccCode',
|
||||||
|
'currAccCode',
|
||||||
|
'accountCode',
|
||||||
|
'vendorCode'
|
||||||
|
])).trim(),
|
||||||
|
companyDescription: String(firstDefinedValue(item, [
|
||||||
|
'FirmaAciklama',
|
||||||
|
'firmaAciklama',
|
||||||
|
'CurrAccDescription',
|
||||||
|
'currAccDescription',
|
||||||
|
'companyDescription',
|
||||||
|
'vendorDescription'
|
||||||
|
])).trim(),
|
||||||
|
itemCode: String(firstDefinedValue(item, [
|
||||||
|
'MasrafKodu',
|
||||||
|
'masrafKodu',
|
||||||
|
'sKodu',
|
||||||
|
's_kodu',
|
||||||
|
'ItemCode',
|
||||||
|
'itemCode'
|
||||||
|
])).trim(),
|
||||||
|
itemDescription: String(firstDefinedValue(item, [
|
||||||
|
'MasrafDetay',
|
||||||
|
'masrafDetay',
|
||||||
|
'sAciklama',
|
||||||
|
's_aciklama',
|
||||||
|
'ItemDescription',
|
||||||
|
'itemDescription',
|
||||||
|
'description'
|
||||||
|
])).trim(),
|
||||||
|
colorCode: String(firstDefinedValue(item, [
|
||||||
|
'ColorCode',
|
||||||
|
'colorCode',
|
||||||
|
'sRenk',
|
||||||
|
's_renk'
|
||||||
|
])).trim(),
|
||||||
|
colorDescription: String(firstDefinedValue(item, [
|
||||||
|
'ColorDescription',
|
||||||
|
'colorDescription',
|
||||||
|
'sRenkAdi',
|
||||||
|
's_renk_adi'
|
||||||
|
])).trim(),
|
||||||
|
itemDim1Code: String(firstDefinedValue(item, [
|
||||||
|
'ItemDim1Code',
|
||||||
|
'itemDim1Code',
|
||||||
|
'sBeden',
|
||||||
|
's_beden'
|
||||||
|
])).trim(),
|
||||||
|
itemDim1Description: String(firstDefinedValue(item, [
|
||||||
|
'ItemDim1Description',
|
||||||
|
'itemDim1Description',
|
||||||
|
'sAciklama2',
|
||||||
|
's_aciklama2'
|
||||||
|
])).trim(),
|
||||||
|
priceType: String(item?.priceType || '').trim(),
|
||||||
|
quantity,
|
||||||
|
unit: String(firstDefinedValue(item, [
|
||||||
|
'BIRIM',
|
||||||
|
'birim',
|
||||||
|
'unit',
|
||||||
|
'Unit',
|
||||||
|
'sBirim',
|
||||||
|
's_birim'
|
||||||
|
])).trim(),
|
||||||
|
price,
|
||||||
|
amount,
|
||||||
|
currency: currency || 'USD',
|
||||||
|
recipeInfo: String(firstDefinedValue(item, [
|
||||||
|
'recipeInfo',
|
||||||
|
'receteBilgisi',
|
||||||
|
'recete',
|
||||||
|
'DUMMY',
|
||||||
|
'nUrtReceteID',
|
||||||
|
'sAciklama3',
|
||||||
|
'Description',
|
||||||
|
'Aciklama',
|
||||||
|
'Aciklama1',
|
||||||
|
'LineDescription',
|
||||||
|
'note'
|
||||||
|
])).trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeBulkPriceItems (payload) {
|
||||||
|
const list = Array.isArray(payload)
|
||||||
|
? payload
|
||||||
|
: extractNamedArray(payload, ['items', 'data', 'updates', 'matchedItems', 'rows'])
|
||||||
|
|
||||||
|
return list.map((item, index) => {
|
||||||
|
const price = parseMoneyInput(firstDefinedValue(item, [
|
||||||
|
'EvrakFiyat',
|
||||||
|
'evrakFiyat',
|
||||||
|
'fiyat_girilen',
|
||||||
|
'fiyatGirilen',
|
||||||
|
'price',
|
||||||
|
'fiyat',
|
||||||
|
'unitPrice',
|
||||||
|
'birimFiyat',
|
||||||
|
'lDovizFiyati',
|
||||||
|
'lFiyat'
|
||||||
|
]))
|
||||||
|
const currency = normalizePriceCurrency(firstDefinedValue(item, [
|
||||||
|
'EvrakDoviz',
|
||||||
|
'evrakDoviz',
|
||||||
|
'fiyat_doviz',
|
||||||
|
'fiyatDoviz',
|
||||||
|
'currency',
|
||||||
|
'priceCurrency',
|
||||||
|
'prBr',
|
||||||
|
'pb',
|
||||||
|
'sDovizCinsi'
|
||||||
|
]))
|
||||||
|
|
||||||
|
return {
|
||||||
|
__updateKey: `upd-${index}`,
|
||||||
|
__rowKey: String(firstDefinedValue(item, ['__rowKey', 'rowKey'])).trim(),
|
||||||
|
nOnMLDetNo: String(firstDefinedValue(item, ['nOnMLDetNo', 'n_onml_det_no'])).trim(),
|
||||||
|
nHammaddeTuruNo: String(firstDefinedValue(item, ['nHammaddeTuruNo', 'n_hammadde_turu_no'])).trim(),
|
||||||
|
sKodu: normalizeCodeValue(firstDefinedValue(item, ['sKodu', 's_kodu', 'stokKodu', 'MasrafKodu', 'masrafKodu', 'ItemCode', 'itemCode'])),
|
||||||
|
colorCode: normalizeCodeValue(firstDefinedValue(item, ['ColorCode', 'colorCode', 'sRenk', 's_renk', 'renk'])),
|
||||||
|
itemDim1Code: normalizeCodeValue(firstDefinedValue(item, ['ItemDim1Code', 'itemDim1Code', 'item_dim1_code', 'sBeden', 's_beden'])),
|
||||||
|
inputPrice: normalizeInputPrice(price),
|
||||||
|
fiyat_girilen: price,
|
||||||
|
inputPricePrBr: currency || 'USD',
|
||||||
|
fiyat_doviz: currency || 'USD'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeLineHistoryRows (payload) {
|
||||||
|
const purchaseRows = extractNamedArray(payload, [
|
||||||
|
'purchaseRows',
|
||||||
|
'purchase_items',
|
||||||
|
'purchaseItems',
|
||||||
|
'invoiceRows',
|
||||||
|
'invoice_items',
|
||||||
|
'invoiceItems',
|
||||||
|
'baggiV3Rows',
|
||||||
|
'baggi_v3_rows',
|
||||||
|
'baggiRows'
|
||||||
|
])
|
||||||
|
const recipeRows = extractNamedArray(payload, [
|
||||||
|
'recipeRows',
|
||||||
|
'recipe_items',
|
||||||
|
'recipeItems',
|
||||||
|
'uretimRows',
|
||||||
|
'uretim_rows',
|
||||||
|
'productionRows'
|
||||||
|
])
|
||||||
|
|
||||||
|
if (purchaseRows.length > 0 || recipeRows.length > 0) {
|
||||||
|
const normalizedPurchaseRows = sortHistoryRowsByDateDesc(
|
||||||
|
purchaseRows.map((item, index) => normalizeHistoryRowFromItem(item, index, 'purchase'))
|
||||||
|
)
|
||||||
|
const normalizedRecipeRows = sortHistoryRowsByDateDesc(
|
||||||
|
recipeRows.map((item, index) => normalizeHistoryRowFromItem(item, index, 'recipe'))
|
||||||
|
)
|
||||||
|
return sortHistoryRowsByDateDesc([
|
||||||
|
...normalizedPurchaseRows,
|
||||||
|
...normalizedRecipeRows
|
||||||
|
]).slice(0, LINE_HISTORY_COMBINED_ROW_LIMIT)
|
||||||
|
}
|
||||||
|
|
||||||
|
const flatList = Array.isArray(payload)
|
||||||
|
? payload
|
||||||
|
: extractNamedArray(payload, ['items', 'data', 'rows'])
|
||||||
|
|
||||||
|
const normalizedRows = flatList.map((item, index) => normalizeHistoryRowFromItem(item, index))
|
||||||
|
const purchaseHistoryRows = sortHistoryRowsByDateDesc(normalizedRows.filter(row => row.sourceType === 'purchase'))
|
||||||
|
const recipeHistoryRows = sortHistoryRowsByDateDesc(normalizedRows.filter(row => row.sourceType === 'recipe'))
|
||||||
|
const otherHistoryRows = sortHistoryRowsByDateDesc(normalizedRows.filter(row => !['purchase', 'recipe'].includes(row.sourceType)))
|
||||||
|
|
||||||
|
return sortHistoryRowsByDateDesc([
|
||||||
|
...purchaseHistoryRows,
|
||||||
|
...recipeHistoryRows,
|
||||||
|
...otherHistoryRows
|
||||||
|
]).slice(0, LINE_HISTORY_COMBINED_ROW_LIMIT)
|
||||||
|
}
|
||||||
|
|
||||||
|
function rowMatchesBulkUpdate (row, update) {
|
||||||
|
if (update.__rowKey && update.__rowKey === row.__rowKey) return true
|
||||||
|
if (update.nOnMLDetNo && String(row?.nOnMLDetNo || '').trim() === update.nOnMLDetNo) return true
|
||||||
|
|
||||||
|
const rowKodu = normalizeCodeValue(row?.sKodu)
|
||||||
|
const rowRenk = resolveRowColorCode(row)
|
||||||
|
const rowTur = String(row?.nHammaddeTuruNo || '').trim()
|
||||||
|
const rowItemDim1Code = resolveRowItemDim1Code(row)
|
||||||
|
|
||||||
|
if (update.nHammaddeTuruNo && update.sKodu) {
|
||||||
|
if (rowTur === update.nHammaddeTuruNo && rowKodu === update.sKodu) return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update.sKodu && update.colorCode && update.itemDim1Code) {
|
||||||
|
if (rowKodu === update.sKodu && rowRenk === update.colorCode && rowItemDim1Code === update.itemDim1Code) return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update.sKodu && update.colorCode) {
|
||||||
|
if (rowKodu === update.sKodu && rowRenk === update.colorCode) return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update.sKodu && update.itemDim1Code) {
|
||||||
|
if (rowKodu === update.sKodu && rowItemDim1Code === update.itemDim1Code) return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update.sKodu) {
|
||||||
|
return rowKodu === update.sKodu
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveExchangeRateValue (currency) {
|
||||||
|
const normalizedCurrency = normalizePriceCurrency(currency)
|
||||||
|
if (normalizedCurrency === 'TRY') return 1
|
||||||
|
if (normalizedCurrency === 'USD') return parseMoneyInput(exchangeRates.value?.usdRate)
|
||||||
|
if (normalizedCurrency === 'EUR') return parseMoneyInput(exchangeRates.value?.eurRate)
|
||||||
|
if (normalizedCurrency === 'GBP') return parseMoneyInput(exchangeRates.value?.gbpRate)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveInputCurrency (row) {
|
||||||
|
return normalizePriceCurrency(row?.inputPricePrBr || row?.fiyat_doviz) || 'USD'
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveNumericRowQuantity (row) {
|
||||||
|
return parseMoneyInput(row?.miktarInput ?? row?.lMiktar)
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveNumericRowInputPrice (row) {
|
||||||
|
return parseMoneyInput(row?.inputPrice ?? row?.fiyat_girilen)
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveTRYUnitPriceByInput (inputPrice, inputCurrency, usdRate, eurRate, gbpRate) {
|
||||||
|
if (inputPrice <= 0) return 0
|
||||||
|
if (inputCurrency === 'TRY') return inputPrice
|
||||||
|
if (inputCurrency === 'USD') return usdRate > 0 ? inputPrice * usdRate : 0
|
||||||
|
if (inputCurrency === 'EUR') return eurRate > 0 ? inputPrice * eurRate : 0
|
||||||
|
if (inputCurrency === 'GBP') return gbpRate > 0 ? inputPrice * gbpRate : 0
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveUSDUnitPriceByInput (inputPrice, inputCurrency, usdRate, eurRate, gbpRate) {
|
||||||
|
if (inputPrice <= 0) return 0
|
||||||
|
if (inputCurrency === 'USD') return inputPrice
|
||||||
|
if (inputCurrency === 'TRY') return usdRate > 0 ? inputPrice / usdRate : 0
|
||||||
|
if (inputCurrency === 'EUR' || inputCurrency === 'GBP') {
|
||||||
|
const tryEquivalent = resolveTRYUnitPriceByInput(inputPrice, inputCurrency, usdRate, eurRate, gbpRate)
|
||||||
|
return usdRate > 0 ? tryEquivalent / usdRate : 0
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeDetailRows (items, groupName = '') {
|
||||||
|
const list = Array.isArray(items) ? items : []
|
||||||
|
return list.map((x, i) => ({
|
||||||
|
...x,
|
||||||
|
__rowKey: x?.__rowKey || `${x?.nOnMLNo || ''}-${x?.nOnMLDetNo || ''}-${i}`,
|
||||||
|
miktarInput: x?.miktarInput ?? normalizeQuantityInput(x?.lMiktar),
|
||||||
|
inputPrice: x?.inputPrice ?? normalizeInputPrice(x?.fiyat_girilen),
|
||||||
|
inputPricePrBr: normalizePriceCurrency(x?.inputPricePrBr || x?.fiyat_doviz || x?.sDovizCinsi) || 'USD',
|
||||||
|
maliyeteDahil: x?.maliyeteDahil ?? normalizeBooleanFlag(x?.maliyete_dahil ?? x?.Maliyete_dahil),
|
||||||
|
cmPriceTypeId: normalizeCMPriceTypeId(x?.cmPriceTypeId ?? x?.cm_price_type_id, groupName || x?.sAciklama3),
|
||||||
|
draftChanged: Boolean(x?.draftChanged),
|
||||||
|
priceUpdateState: String(x?.priceUpdateState || '').trim()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeDetailGroups (groups) {
|
||||||
|
const list = Array.isArray(groups) ? groups : []
|
||||||
|
return list.map(grp => {
|
||||||
|
const groupName = String(grp?.sAciklama3 || '').trim()
|
||||||
|
const items = normalizeDetailRows(grp?.items, groupName).map(row => ({
|
||||||
|
...row,
|
||||||
|
sAciklama3: String(grp?.sAciklama3 || row?.sAciklama3 || '').trim(),
|
||||||
|
cmPriceTypeId: normalizeCMPriceTypeId(row?.cmPriceTypeId ?? row?.cm_price_type_id, groupName || row?.sAciklama3)
|
||||||
|
}))
|
||||||
|
// USD TUTAR (DESC) sıralama
|
||||||
|
items.sort((a, b) => {
|
||||||
|
const valA = resolveRowUSDTutar(a)
|
||||||
|
const valB = resolveRowUSDTutar(b)
|
||||||
|
return valB - valA
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
...grp,
|
||||||
|
items
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function recalculateDetailRow (row, options = {}) {
|
||||||
|
const quantity = resolveNumericRowQuantity(row)
|
||||||
|
const inputPrice = resolveNumericRowInputPrice(row)
|
||||||
|
const inputCurrency = resolveInputCurrency(row)
|
||||||
|
const usdRate = resolveExchangeRateValue('USD')
|
||||||
|
const eurRate = resolveExchangeRateValue('EUR')
|
||||||
|
const gbpRate = resolveExchangeRateValue('GBP')
|
||||||
|
const unitTRY = resolveTRYUnitPriceByInput(inputPrice, inputCurrency, usdRate, eurRate, gbpRate)
|
||||||
|
const unitUSD = resolveUSDUnitPriceByInput(inputPrice, inputCurrency, usdRate, eurRate, gbpRate)
|
||||||
|
const unitEUR = eurRate > 0 ? unitTRY / eurRate : 0
|
||||||
|
const unitGBP = gbpRate > 0 ? unitTRY / gbpRate : 0
|
||||||
|
const maliyeteDahil = normalizeBooleanFlag(row?.maliyeteDahil ?? row?.maliyete_dahil ?? row?.Maliyete_dahil)
|
||||||
|
const cmPriceTypeId = normalizeCMPriceTypeId(row?.cmPriceTypeId ?? row?.cm_price_type_id, row?.sAciklama3)
|
||||||
|
|
||||||
|
row.lMiktar = quantity
|
||||||
|
row.fiyat_girilen = inputPrice
|
||||||
|
row.fiyat_doviz = inputCurrency
|
||||||
|
row.inputPricePrBr = inputCurrency
|
||||||
|
row.maliyeteDahil = maliyeteDahil
|
||||||
|
row.maliyete_dahil = maliyeteDahil ? 1 : 0
|
||||||
|
row.Maliyete_dahil = maliyeteDahil ? 1 : 0
|
||||||
|
row.cmPriceTypeId = cmPriceTypeId
|
||||||
|
row.cm_price_type_id = cmPriceTypeId
|
||||||
|
row.sDovizCinsi = 'USD'
|
||||||
|
row.lDovizKuru = usdRate
|
||||||
|
row.lDovizFiyati = unitUSD
|
||||||
|
row.lFiyat = unitTRY
|
||||||
|
row.lTutar = unitTRY * quantity
|
||||||
|
row.usdTutar = unitUSD * quantity
|
||||||
|
row.eurTutar = unitEUR * quantity
|
||||||
|
row.gbpTutar = unitGBP * quantity
|
||||||
|
row.lTutarUSD = row.usdTutar
|
||||||
|
row.lTutarEURO = row.eurTutar
|
||||||
|
row.lTutarGBP = row.gbpTutar
|
||||||
|
row.lTutarTL = row.lTutar
|
||||||
|
|
||||||
|
if (!options.preserveInputs) {
|
||||||
|
row.miktarInput = normalizeQuantityInput(quantity)
|
||||||
|
row.inputPrice = normalizeInputPrice(inputPrice)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.markChanged) {
|
||||||
|
row.draftChanged = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.priceType !== undefined) {
|
||||||
|
row.sFiyatTipi = options.priceType
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.updateState !== undefined) {
|
||||||
|
row.priceUpdateState = options.updateState
|
||||||
|
}
|
||||||
|
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
|
||||||
|
function recalculateAllDetailRows () {
|
||||||
|
detailGroups.value = detailGroups.value.map(grp => ({
|
||||||
|
...grp,
|
||||||
|
items: (Array.isArray(grp?.items) ? grp.items : []).map(row => recalculateDetailRow({ ...row }, { preserveInputs: true }))
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveRowTRYTutar (row) {
|
||||||
|
const tryAmount = Number(row?.lTutar || 0)
|
||||||
|
return Number.isFinite(tryAmount) ? tryAmount : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveRowEURTutar (row) {
|
||||||
|
const eurAmount = Number(row?.eurTutar || 0)
|
||||||
|
return Number.isFinite(eurAmount) ? eurAmount : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveRowGBPTutar (row) {
|
||||||
|
const gbpAmount = Number(row?.gbpTutar || 0)
|
||||||
|
return Number.isFinite(gbpAmount) ? gbpAmount : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveRowUSDTutar (row) {
|
||||||
|
const usdAmount = Number(row?.usdTutar || 0)
|
||||||
|
if (Number.isFinite(usdAmount)) return usdAmount
|
||||||
|
|
||||||
|
const miktar = resolveNumericRowQuantity(row)
|
||||||
|
const dovizFiyati = Number(row?.lDovizFiyati || 0)
|
||||||
|
const calc = miktar * dovizFiyati
|
||||||
|
return Number.isFinite(calc) ? calc : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldIgnoreGroupMaliyeteDahil (grp) {
|
||||||
|
return isCMGroupName(grp?.sAciklama3)
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldIncludeRowInGroupTotal (grp, row) {
|
||||||
|
return shouldIgnoreGroupMaliyeteDahil(grp) || normalizeBooleanFlag(row?.maliyeteDahil)
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveGroupTRYTutar (grp) {
|
||||||
|
const items = Array.isArray(grp?.items) ? grp.items : []
|
||||||
|
return items.reduce((acc, row) => acc + (shouldIncludeRowInGroupTotal(grp, row) ? resolveRowTRYTutar(row) : 0), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveGroupUSDTutar (grp) {
|
||||||
|
const items = Array.isArray(grp?.items) ? grp.items : []
|
||||||
|
return items.reduce((acc, row) => acc + (shouldIncludeRowInGroupTotal(grp, row) ? resolveRowUSDTutar(row) : 0), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function groupKey (grp, gi) {
|
||||||
|
return `${String(grp?.sAciklama3 || 'TANIMSIZ')}-${gi}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function isGroupOpen (grp, gi) {
|
||||||
|
return groupOpenState.value[groupKey(grp, gi)] !== false
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleGroup (grp, gi) {
|
||||||
|
const key = groupKey(grp, gi)
|
||||||
|
groupOpenState.value = {
|
||||||
|
...groupOpenState.value,
|
||||||
|
[key]: !isGroupOpen(grp, gi)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveElHeight (refVal) {
|
||||||
|
const el = refVal?.$el || refVal
|
||||||
|
return Number(el?.offsetHeight || 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateStickyTop () {
|
||||||
|
const stackH = resolveElHeight(stickyStackRef.value)
|
||||||
|
// Quasar default header height is usually around 50px
|
||||||
|
// If we are in a sub-layout or context where top header is not 50px, this might need adjustment
|
||||||
|
const layoutHeader = document.querySelector('.q-header')
|
||||||
|
const layoutHeaderH = layoutHeader ? layoutHeader.offsetHeight : 50
|
||||||
|
subHeaderTop.value = (stackH || 0) + layoutHeaderH
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleHeaderInfo () {
|
||||||
|
headerInfoCollapsed.value = !headerInfoCollapsed.value
|
||||||
|
nextTick(() => {
|
||||||
|
updateStickyTop()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function onUretimSekliChange (newId) {
|
||||||
|
if (!detailHeader.value) return
|
||||||
|
const found = productionTypes.value.find(t => String(t.id) === String(newId))
|
||||||
|
if (found) {
|
||||||
|
detailHeader.value.UretimSekliID = found.id
|
||||||
|
detailHeader.value.UretimSekli = found.aciklama
|
||||||
|
schedulePersistLocalDraft()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildDetailFetchParams () {
|
||||||
|
if (isNoCostDetail.value) {
|
||||||
|
return {
|
||||||
|
detail_source: 'no-cost',
|
||||||
|
urun_kodu: productCode.value,
|
||||||
|
recete_kodu: recipeCode.value,
|
||||||
|
trace_id: traceId.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
n_onml_no: onMLNo.value,
|
||||||
|
urun_kodu: productCode.value,
|
||||||
|
trace_id: traceId.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchExchangeRatesForCostDate (targetDate = costDate.value) {
|
||||||
|
const normalizedDate = normalizeDateInput(targetDate)
|
||||||
|
if (!normalizedDate) {
|
||||||
|
exchangeRates.value = createEmptyExchangeRates()
|
||||||
|
recalculateAllDetailRows()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await get('/pricing/production-product-costing/has-cost-detail-exchange-rates', {
|
||||||
|
maliyet_tarihi: normalizedDate,
|
||||||
|
trace_id: traceId.value
|
||||||
|
})
|
||||||
|
exchangeRates.value = normalizeExchangeRatesPayload(response, normalizedDate)
|
||||||
|
} catch (err) {
|
||||||
|
exchangeRates.value = {
|
||||||
|
...createEmptyExchangeRates(),
|
||||||
|
rateDate: normalizedDate
|
||||||
|
}
|
||||||
|
slog.error('production-product-costing.detail', 'exchange-rates:error', {
|
||||||
|
trace_id: traceId.value,
|
||||||
|
maliyet_tarihi: normalizedDate,
|
||||||
|
detail: await extractApiErrorDetail(err)
|
||||||
|
})
|
||||||
|
$q.notify({
|
||||||
|
type: 'negative',
|
||||||
|
message: await extractApiErrorDetail(err),
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
recalculateAllDetailRows()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchDetail () {
|
||||||
|
if (isNoCostDetail.value && !recipeCode.value) {
|
||||||
|
detailError.value = 'Recete kodu bulunamadi'
|
||||||
|
detailGroups.value = []
|
||||||
|
detailHeader.value = null
|
||||||
|
exchangeRates.value = createEmptyExchangeRates()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isNoCostDetail.value && !onMLNo.value) {
|
||||||
|
detailError.value = 'nOnMLNo bulunamadi'
|
||||||
|
detailGroups.value = []
|
||||||
|
detailHeader.value = null
|
||||||
|
exchangeRates.value = createEmptyExchangeRates()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
detailLoading.value = true
|
||||||
|
detailError.value = ''
|
||||||
|
detailGroups.value = []
|
||||||
|
detailHeader.value = null
|
||||||
|
costDate.value = ''
|
||||||
|
exchangeRates.value = createEmptyExchangeRates()
|
||||||
|
newRowSequence.value = 0
|
||||||
|
rowEditorDialogOpen.value = false
|
||||||
|
rowEditorMode.value = 'new'
|
||||||
|
rowEditorTargetRowKey.value = ''
|
||||||
|
rowEditorForm.value = createRowEditorForm()
|
||||||
|
rowEditorHammaddeAllOptions.value = []
|
||||||
|
rowEditorHammaddeOptions.value = []
|
||||||
|
rowEditorHammaddeLoading.value = false
|
||||||
|
rowEditorItemAllOptions.value = []
|
||||||
|
rowEditorItemOptions.value = []
|
||||||
|
rowEditorItemLoading.value = false
|
||||||
|
rowEditorColorAllOptions.value = []
|
||||||
|
rowEditorColorOptions.value = []
|
||||||
|
rowEditorColorLoading.value = false
|
||||||
|
lineHistoryDialogOpen.value = false
|
||||||
|
lineHistoryLoading.value = false
|
||||||
|
lineHistoryError.value = ''
|
||||||
|
lineHistoryRows.value = []
|
||||||
|
lineHistoryTargetRowKey.value = ''
|
||||||
|
lineHistoryTargetHammaddeTuruNo.value = ''
|
||||||
|
lineHistoryTargetItemCode.value = ''
|
||||||
|
lineHistoryTargetSummary.value = ''
|
||||||
|
lineHistorySearchMode.value = 'exact'
|
||||||
|
lineHistoryLastPurchaseMatchStage.value = ''
|
||||||
|
lineHistoryLastRecipeMatchStage.value = ''
|
||||||
|
|
||||||
|
try {
|
||||||
|
const detailParams = buildDetailFetchParams()
|
||||||
|
slog.info('production-product-costing.detail', 'fetch-detail:start', {
|
||||||
|
trace_id: traceId.value,
|
||||||
|
detail_source: detailSource.value || 'has-cost',
|
||||||
|
n_onml_no: onMLNo.value,
|
||||||
|
urun_kodu: productCode.value,
|
||||||
|
recete_kodu: recipeCode.value
|
||||||
|
})
|
||||||
|
const [headerData, groupsData, typesData] = await Promise.all([
|
||||||
|
get('/pricing/production-product-costing/has-cost-detail-header', detailParams),
|
||||||
|
get('/pricing/production-product-costing/has-cost-detail-groups', detailParams),
|
||||||
|
get('/pricing/production-product-costing/production-types', {
|
||||||
|
trace_id: traceId.value
|
||||||
|
})
|
||||||
|
])
|
||||||
|
detailHeader.value = headerData && typeof headerData === 'object' ? headerData : null
|
||||||
|
productionTypes.value = Array.isArray(typesData) ? typesData : []
|
||||||
|
costDate.value = normalizeDateInput(detailHeader.value?.dteKayitTarihi)
|
||||||
|
detailGroups.value = normalizeDetailGroups(groupsData)
|
||||||
|
initialHeaderSnapshot.value = currentHeaderSnapshot.value
|
||||||
|
// Optional: hydrate local draft after base data load.
|
||||||
|
tryHydrateFromLocalDraft()
|
||||||
|
// ensure required placeholder rows exist (based on mapping screen)
|
||||||
|
try {
|
||||||
|
const mappings = await fetchRequiredParcaMappings()
|
||||||
|
ensureNoCostRequiredRowsFromMappings(mappings)
|
||||||
|
} catch (err) {
|
||||||
|
slog.error('production-product-costing.detail', 'required-mapping:error', {
|
||||||
|
trace_id: traceId.value,
|
||||||
|
detail: await extractApiErrorDetail(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const initialOpen = {}
|
||||||
|
detailGroups.value.forEach((grp, gi) => {
|
||||||
|
initialOpen[groupKey(grp, gi)] = true
|
||||||
|
})
|
||||||
|
groupOpenState.value = initialOpen
|
||||||
|
slog.info('production-product-costing.detail', 'fetch-detail:ok', {
|
||||||
|
trace_id: traceId.value,
|
||||||
|
detail_source: detailSource.value || 'has-cost',
|
||||||
|
group_count: detailGroups.value.length,
|
||||||
|
item_count: flatDetailRows.value.length,
|
||||||
|
urun_kodu: detailHeader.value?.UrunKodu || productCode.value,
|
||||||
|
n_urt_recete_id: detailHeader.value?.nUrtReceteID || ''
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
detailError.value = await extractApiErrorDetail(err)
|
||||||
|
slog.error('production-product-costing.detail', 'fetch-detail:error', {
|
||||||
|
trace_id: traceId.value,
|
||||||
|
detail_source: detailSource.value || 'has-cost',
|
||||||
|
n_onml_no: onMLNo.value,
|
||||||
|
urun_kodu: productCode.value,
|
||||||
|
recete_kodu: recipeCode.value,
|
||||||
|
detail: detailError.value
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
detailLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function goBack () {
|
||||||
|
if (isNoCostDetail.value) {
|
||||||
|
router.push({ name: 'production-product-costing-no-cost' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const urunKodu = productCode.value
|
||||||
|
if (!urunKodu) {
|
||||||
|
router.push({ name: 'production-product-costing-has-cost' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
router.push({
|
||||||
|
name: 'production-product-costing-has-cost-history',
|
||||||
|
query: { urun_kodu: urunKodu }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildDetailItemRequestPayload (row) {
|
||||||
|
return {
|
||||||
|
__rowKey: row.__rowKey,
|
||||||
|
n_onml_no: String(row?.nOnMLNo || detailHeader.value?.nOnMLNo || onMLNo.value || '').trim(),
|
||||||
|
n_onml_det_no: String(row?.nOnMLDetNo || '').trim(),
|
||||||
|
n_hammadde_turu_no: String(row?.nHammaddeTuruNo || '').trim(),
|
||||||
|
s_kodu: String(row?.sKodu || '').trim(),
|
||||||
|
s_aciklama: String(row?.sAciklama || '').trim(),
|
||||||
|
s_renk: String(row?.sRenk || '').trim(),
|
||||||
|
color_code: String(firstDefinedValue(row, ['ColorCode', 'colorCode', 'sRenk', 's_renk'])).trim(),
|
||||||
|
color_description: String(firstDefinedValue(row, ['ColorDescription', 'colorDescription', 'sRenkAdi', 's_renk_adi'])).trim(),
|
||||||
|
item_dim1_code: String(firstDefinedValue(row, ['ItemDim1Code', 'itemDim1Code', 'sBeden', 's_beden'])).trim(),
|
||||||
|
item_dim1_description: String(firstDefinedValue(row, ['ItemDim1Description', 'itemDim1Description', 'sAciklama2', 's_aciklama2'])).trim(),
|
||||||
|
s_birim: String(row?.sBirim || '').trim(),
|
||||||
|
l_miktar: resolveNumericRowQuantity(row),
|
||||||
|
fiyat_girilen: resolveNumericRowInputPrice(row),
|
||||||
|
fiyat_doviz: resolveInputCurrency(row),
|
||||||
|
maliyete_dahil: row?.maliyeteDahil ? 1 : 0,
|
||||||
|
cm_price_type_id: normalizeCMPriceTypeId(row?.cmPriceTypeId ?? row?.cm_price_type_id, row?.sAciklama3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyPriceSelectionToRow (targetRowKey, price, currency, priceType) {
|
||||||
|
const normalizedCurrency = normalizePriceCurrency(currency) || 'USD'
|
||||||
|
const normalizedPrice = parseMoneyInput(price)
|
||||||
|
const finalPriceType = priceType || 'SAF'
|
||||||
|
|
||||||
|
detailGroups.value = detailGroups.value.map(grp => ({
|
||||||
|
...grp,
|
||||||
|
items: (Array.isArray(grp?.items) ? grp.items : []).map(row => {
|
||||||
|
if (row.__rowKey !== targetRowKey) return row
|
||||||
|
return recalculateDetailRow({
|
||||||
|
...row,
|
||||||
|
inputPrice: normalizeInputPrice(normalizedPrice),
|
||||||
|
fiyat_girilen: normalizedPrice,
|
||||||
|
inputPricePrBr: normalizedCurrency,
|
||||||
|
fiyat_doviz: normalizedCurrency
|
||||||
|
}, {
|
||||||
|
priceType: finalPriceType,
|
||||||
|
updateState: 'selection',
|
||||||
|
markChanged: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchSimilarItemHistory (mode = 'prefix') {
|
||||||
|
if (mode === 'exact') {
|
||||||
|
const row = flatDetailRows.value.find(r => r.__rowKey === lineHistoryTargetRowKey.value)
|
||||||
|
if (row) {
|
||||||
|
await openLineHistory(row)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lineHistoryTargetHammaddeTuruNo.value && !lineHistoryTargetItemCode.value) return
|
||||||
|
|
||||||
|
const normalizedMode = mode === 'alternative' ? 'alternative' : 'prefix'
|
||||||
|
lineHistorySearchMode.value = normalizedMode
|
||||||
|
lineHistoryLoading.value = true
|
||||||
|
lineHistoryError.value = ''
|
||||||
|
|
||||||
|
try {
|
||||||
|
slog.info('production-product-costing.detail', 'line-history-similar:start', {
|
||||||
|
trace_id: traceId.value,
|
||||||
|
search_mode: normalizedMode,
|
||||||
|
n_hammadde_turu_no: lineHistoryTargetHammaddeTuruNo.value,
|
||||||
|
s_kodu: lineHistoryTargetItemCode.value,
|
||||||
|
maliyet_tarihi: costDate.value,
|
||||||
|
limit: LINE_HISTORY_ROW_LIMIT
|
||||||
|
})
|
||||||
|
|
||||||
|
const response = await get('/pricing/production-product-costing/has-cost-detail-similar-history', {
|
||||||
|
n_hammadde_turu_no: lineHistoryTargetHammaddeTuruNo.value,
|
||||||
|
s_kodu: lineHistoryTargetItemCode.value,
|
||||||
|
maliyet_tarihi: costDate.value,
|
||||||
|
limit: LINE_HISTORY_ROW_LIMIT,
|
||||||
|
search_mode: normalizedMode,
|
||||||
|
trace_id: traceId.value
|
||||||
|
})
|
||||||
|
|
||||||
|
lineHistoryRows.value = normalizeLineHistoryRows(response)
|
||||||
|
lineHistoryLastPurchaseMatchStage.value = String(response?.purchase_match_stage || '').trim()
|
||||||
|
lineHistoryLastRecipeMatchStage.value = String(response?.recipe_match_stage || '').trim()
|
||||||
|
slog.info('production-product-costing.detail', 'line-history-similar:ok', {
|
||||||
|
trace_id: traceId.value,
|
||||||
|
search_mode: normalizedMode,
|
||||||
|
purchase_match_stage: response?.purchase_match_stage || '',
|
||||||
|
recipe_match_stage: response?.recipe_match_stage || '',
|
||||||
|
similar_code_prefix: response?.similar_code_prefix || '',
|
||||||
|
row_count: lineHistoryRows.value.length
|
||||||
|
})
|
||||||
|
|
||||||
|
if (lineHistoryRows.value.length === 0) {
|
||||||
|
$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Bu hammadde türü için de benzer ürün kaydı bulunamadı.',
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
lineHistoryError.value = await extractApiErrorDetail(err)
|
||||||
|
slog.error('production-product-costing.detail', 'line-history-similar:error', {
|
||||||
|
trace_id: traceId.value,
|
||||||
|
search_mode: normalizedMode,
|
||||||
|
n_hammadde_turu_no: lineHistoryTargetHammaddeTuruNo.value,
|
||||||
|
s_kodu: lineHistoryTargetItemCode.value,
|
||||||
|
error: lineHistoryError.value
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
lineHistoryLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRowQuantityInput (row, value) {
|
||||||
|
row.miktarInput = value
|
||||||
|
recalculateDetailRow(row, {
|
||||||
|
preserveInputs: true,
|
||||||
|
markChanged: true
|
||||||
|
})
|
||||||
|
schedulePersistLocalDraft()
|
||||||
|
triggerUIUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
function triggerUIUpdate () {
|
||||||
|
detailGroups.value = [...detailGroups.value]
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeRowQuantityDisplay (row) {
|
||||||
|
row.miktarInput = normalizeQuantityInput(row?.miktarInput)
|
||||||
|
recalculateDetailRow(row, {
|
||||||
|
preserveInputs: true
|
||||||
|
})
|
||||||
|
schedulePersistLocalDraft()
|
||||||
|
triggerUIUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRowInputPriceChange (row, value) {
|
||||||
|
row.inputPrice = value
|
||||||
|
recalculateDetailRow(row, {
|
||||||
|
preserveInputs: true,
|
||||||
|
priceType: 'MAN',
|
||||||
|
updateState: 'manual',
|
||||||
|
markChanged: true
|
||||||
|
})
|
||||||
|
schedulePersistLocalDraft()
|
||||||
|
triggerUIUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeRowPriceDisplay (row) {
|
||||||
|
row.inputPrice = normalizeInputPrice(row?.inputPrice)
|
||||||
|
recalculateDetailRow(row, {
|
||||||
|
preserveInputs: true
|
||||||
|
})
|
||||||
|
schedulePersistLocalDraft()
|
||||||
|
triggerUIUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRowInputPriceCurrencyChange (row, value) {
|
||||||
|
row.inputPricePrBr = normalizePriceCurrency(value) || 'USD'
|
||||||
|
recalculateDetailRow(row, {
|
||||||
|
preserveInputs: true,
|
||||||
|
priceType: 'MAN',
|
||||||
|
updateState: 'manual',
|
||||||
|
markChanged: true
|
||||||
|
})
|
||||||
|
schedulePersistLocalDraft()
|
||||||
|
triggerUIUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRowMaliyeteDahilChange (row, value) {
|
||||||
|
row.maliyeteDahil = Boolean(value)
|
||||||
|
row.maliyete_dahil = value ? 1 : 0
|
||||||
|
row.Maliyete_dahil = value ? 1 : 0
|
||||||
|
row.draftChanged = true
|
||||||
|
schedulePersistLocalDraft()
|
||||||
|
triggerUIUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRowCMPriceTypeChange (row, value) {
|
||||||
|
row.cmPriceTypeId = isCMGroupName(row?.sAciklama3) ? (value ? 2 : 1) : null
|
||||||
|
row.cm_price_type_id = row.cmPriceTypeId
|
||||||
|
row.draftChanged = true
|
||||||
|
schedulePersistLocalDraft()
|
||||||
|
triggerUIUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchBulkItemPrices () {
|
||||||
|
const flatRows = detailGroups.value.flatMap(grp => Array.isArray(grp?.items) ? grp.items : [])
|
||||||
|
if (flatRows.length === 0) {
|
||||||
|
$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Toplu fiyat cagrisi icin satir bulunamadi.',
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bulkPriceLoading.value = true
|
||||||
|
try {
|
||||||
|
const response = await post('/pricing/production-product-costing/has-cost-detail-bulk-prices', {
|
||||||
|
n_onml_no: onMLNo.value,
|
||||||
|
urun_kodu: detailHeader.value?.UrunKodu || productCode.value,
|
||||||
|
n_urt_recete_id: detailHeader.value?.nUrtReceteID || '',
|
||||||
|
maliyet_tarihi: costDate.value,
|
||||||
|
items: flatRows.map(buildDetailItemRequestPayload)
|
||||||
|
}, {
|
||||||
|
params: {
|
||||||
|
trace_id: traceId.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const updates = normalizeBulkPriceItems(response)
|
||||||
|
if (updates.length === 0) {
|
||||||
|
$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Toplu fiyat cagrisindan uygulanacak veri donmedi.',
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let appliedCount = 0
|
||||||
|
detailGroups.value = detailGroups.value.map(grp => ({
|
||||||
|
...grp,
|
||||||
|
items: (Array.isArray(grp?.items) ? grp.items : []).map(row => {
|
||||||
|
const match = updates.find(update => rowMatchesBulkUpdate(row, update))
|
||||||
|
if (!match) return row
|
||||||
|
appliedCount += 1
|
||||||
|
return recalculateDetailRow({
|
||||||
|
...row,
|
||||||
|
inputPrice: match.inputPrice,
|
||||||
|
fiyat_girilen: match.fiyat_girilen,
|
||||||
|
inputPricePrBr: match.inputPricePrBr,
|
||||||
|
fiyat_doviz: match.fiyat_doviz
|
||||||
|
}, {
|
||||||
|
priceType: match.priceType || 'SAF',
|
||||||
|
updateState: 'bulk',
|
||||||
|
markChanged: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}))
|
||||||
|
|
||||||
|
$q.notify({
|
||||||
|
type: appliedCount > 0 ? 'positive' : 'warning',
|
||||||
|
message: appliedCount > 0
|
||||||
|
? `${appliedCount} kalemin fiyat ve pr. br. bilgisi guncellendi.`
|
||||||
|
: 'Donen veriler satirlarla eslestirilemedi.',
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
$q.notify({
|
||||||
|
type: 'negative',
|
||||||
|
message: await extractApiErrorDetail(err),
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
bulkPriceLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openLineHistory (row) {
|
||||||
|
const rowCode = String(row?.sKodu || '').trim()
|
||||||
|
if (!rowCode) {
|
||||||
|
$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'History acmak icin satirda kod bilgisi olmali.',
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lineHistoryDialogOpen.value = true
|
||||||
|
lineHistoryLoading.value = true
|
||||||
|
lineHistoryError.value = ''
|
||||||
|
lineHistoryRows.value = []
|
||||||
|
lineHistoryTargetRowKey.value = row.__rowKey
|
||||||
|
lineHistoryTargetHammaddeTuruNo.value = String(row?.nHammaddeTuruNo || '').trim()
|
||||||
|
lineHistoryTargetItemCode.value = rowCode
|
||||||
|
lineHistoryTargetSummary.value = `${rowCode} | ${String(row?.sAciklama || '').trim() || 'TANIMSIZ'}`
|
||||||
|
lineHistorySearchMode.value = 'exact'
|
||||||
|
lineHistoryLastPurchaseMatchStage.value = ''
|
||||||
|
lineHistoryLastRecipeMatchStage.value = ''
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await get('/pricing/production-product-costing/has-cost-detail-line-history', {
|
||||||
|
n_onml_no: onMLNo.value,
|
||||||
|
urun_kodu: detailHeader.value?.UrunKodu || productCode.value,
|
||||||
|
n_urt_recete_id: detailHeader.value?.nUrtReceteID || '',
|
||||||
|
n_onml_det_no: String(row?.nOnMLDetNo || '').trim(),
|
||||||
|
n_hammadde_turu_no: String(row?.nHammaddeTuruNo || '').trim(),
|
||||||
|
s_kodu: rowCode,
|
||||||
|
s_renk: String(row?.sRenk || '').trim(),
|
||||||
|
color_code: String(firstDefinedValue(row, ['ColorCode', 'colorCode', 'sRenk', 's_renk'])).trim(),
|
||||||
|
item_dim1_code: String(firstDefinedValue(row, ['ItemDim1Code', 'itemDim1Code', 'sBeden', 's_beden'])).trim(),
|
||||||
|
maliyet_tarihi: costDate.value,
|
||||||
|
trace_id: traceId.value
|
||||||
|
})
|
||||||
|
lineHistoryRows.value = normalizeLineHistoryRows(response)
|
||||||
|
} catch (err) {
|
||||||
|
lineHistoryError.value = await extractApiErrorDetail(err)
|
||||||
|
} finally {
|
||||||
|
lineHistoryLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDetailRowClick (evt, row) {
|
||||||
|
// Grid satırı tıklanabilir değil, artık ikonlar ve butonlar kullanılıyor.
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyLineHistorySelection (historyRow) {
|
||||||
|
applyPriceSelectionToRow(lineHistoryTargetRowKey.value, historyRow?.price, historyRow?.currency, historyRow?.priceType)
|
||||||
|
lineHistoryDialogOpen.value = false
|
||||||
|
$q.notify({
|
||||||
|
type: 'positive',
|
||||||
|
message: 'Secilen history fiyati satira uygulandi.',
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveDetailRowClass (row) {
|
||||||
|
const key = String(row?.__rowKey || '').trim()
|
||||||
|
if (key && requiredAttentionRowKeys.value?.[key]) return 'pcd-detail-row-required'
|
||||||
|
if (row?.requiredPlaceholder) return 'pcd-detail-row-required'
|
||||||
|
return row?.draftChanged ? 'pcd-detail-row-secondary' : ''
|
||||||
|
}
|
||||||
|
|
||||||
|
async function bootstrapRowEditorOptions () {
|
||||||
|
try {
|
||||||
|
const hammaddeSearch = String(rowEditorForm.value.nHammaddeTuruNo || '').trim()
|
||||||
|
const itemSearch = String(rowEditorForm.value.sKodu || '').trim()
|
||||||
|
const colorSearch = String(rowEditorForm.value.ColorCode || '').trim()
|
||||||
|
const [hammaddeRows, itemRows] = await Promise.all([
|
||||||
|
refreshRowEditorOptions('hammadde', hammaddeSearch),
|
||||||
|
itemSearch.length >= ROW_EDITOR_ITEM_MIN_SEARCH_LENGTH
|
||||||
|
? refreshRowEditorOptions('item', itemSearch)
|
||||||
|
: Promise.resolve([])
|
||||||
|
])
|
||||||
|
if (itemSearch.length < ROW_EDITOR_ITEM_MIN_SEARCH_LENGTH) {
|
||||||
|
rowEditorItemAllOptions.value = []
|
||||||
|
rowEditorItemOptions.value = []
|
||||||
|
primeRowEditorOptionsFromForm()
|
||||||
|
}
|
||||||
|
const selectedHammadde = hammaddeRows.find(opt => String(opt?.value || '') === hammaddeSearch)
|
||||||
|
if (selectedHammadde) {
|
||||||
|
rowEditorForm.value.sHammaddeTuruAdi = String(selectedHammadde.sHammaddeTuruAdi || rowEditorForm.value.sHammaddeTuruAdi || '').trim()
|
||||||
|
rowEditorForm.value.sAciklama3 = String(selectedHammadde.sAciklama3 || rowEditorForm.value.sAciklama3 || 'TANIMSIZ').trim() || 'TANIMSIZ'
|
||||||
|
rowEditorForm.value.sParcaAdi = String(selectedHammadde.sParcaAdi || selectedHammadde.sAciklama3 || rowEditorForm.value.sParcaAdi || '').trim()
|
||||||
|
}
|
||||||
|
const selectedItem = itemRows.find(opt => String(opt?.value || '') === itemSearch)
|
||||||
|
if (selectedItem) {
|
||||||
|
rowEditorForm.value.nStokID = String(selectedItem.nStokID || rowEditorForm.value.nStokID || '').trim()
|
||||||
|
rowEditorForm.value.sModel = String(selectedItem.sModel || rowEditorForm.value.sModel || '').trim()
|
||||||
|
rowEditorForm.value.sAciklama = String(selectedItem.sAciklama || rowEditorForm.value.sAciklama || '').trim()
|
||||||
|
rowEditorForm.value.sBirim = extractPrimaryUnitValue(selectedItem.sBirim || rowEditorForm.value.sBirim || 'AD') || 'AD'
|
||||||
|
}
|
||||||
|
await refreshRowEditorOptions('color', colorSearch)
|
||||||
|
primeRowEditorOptionsFromForm()
|
||||||
|
} catch (err) {
|
||||||
|
rowEditorHammaddeAllOptions.value = []
|
||||||
|
rowEditorHammaddeOptions.value = []
|
||||||
|
rowEditorItemAllOptions.value = []
|
||||||
|
rowEditorItemOptions.value = []
|
||||||
|
rowEditorColorAllOptions.value = []
|
||||||
|
rowEditorColorOptions.value = []
|
||||||
|
$q.notify({
|
||||||
|
type: 'negative',
|
||||||
|
message: await extractApiErrorDetail(err),
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRowEditorHammaddeChange (value) {
|
||||||
|
const selected = rowEditorHammaddeOptions.value.find(opt => String(opt?.value || '') === String(value || ''))
|
||||||
|
rowEditorForm.value.nHammaddeTuruNo = String(value || '').trim()
|
||||||
|
if (!selected) return
|
||||||
|
rowEditorForm.value.sHammaddeTuruAdi = String(selected.sHammaddeTuruAdi || '').trim()
|
||||||
|
rowEditorForm.value.sAciklama3 = String(selected.sAciklama3 || 'TANIMSIZ').trim() || 'TANIMSIZ'
|
||||||
|
rowEditorForm.value.sParcaAdi = String(selected.sParcaAdi || selected.sAciklama3 || '').trim()
|
||||||
|
if (!isCMGroupName(rowEditorForm.value.sAciklama3)) {
|
||||||
|
rowEditorForm.value.cmPriceTypeChecked = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onRowEditorItemChange (value) {
|
||||||
|
const selected = rowEditorItemOptions.value.find(opt => String(opt?.value || '') === String(value || ''))
|
||||||
|
rowEditorForm.value.sKodu = String(value || '').trim()
|
||||||
|
if (!selected) return
|
||||||
|
const previousColorCode = String(rowEditorForm.value.ColorCode || '').trim()
|
||||||
|
rowEditorForm.value.nStokID = String(selected.nStokID || '').trim()
|
||||||
|
rowEditorForm.value.sModel = String(selected.sModel || '').trim()
|
||||||
|
rowEditorForm.value.sAciklama = String(selected.sAciklama || '').trim()
|
||||||
|
rowEditorForm.value.sBirim = extractPrimaryUnitValue(selected.sBirim || rowEditorForm.value.sBirim || 'AD') || 'AD'
|
||||||
|
rowEditorForm.value.ColorCode = ''
|
||||||
|
rowEditorForm.value.sRenk = ''
|
||||||
|
rowEditorForm.value.ColorDescription = ''
|
||||||
|
try {
|
||||||
|
await refreshRowEditorOptions('color', previousColorCode)
|
||||||
|
} catch (err) {
|
||||||
|
$q.notify({
|
||||||
|
type: 'negative',
|
||||||
|
message: `Renk lookup getirilemedi: ${await extractApiErrorDetail(err)}`,
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const matchedColor = rowEditorColorOptions.value.find(opt => String(opt?.value || '') === previousColorCode)
|
||||||
|
if (matchedColor) {
|
||||||
|
rowEditorForm.value.ColorCode = previousColorCode
|
||||||
|
rowEditorForm.value.sRenk = previousColorCode
|
||||||
|
rowEditorForm.value.ColorDescription = String(matchedColor.colorDescription || '').trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onRowEditorColorChange (value) {
|
||||||
|
const selected = rowEditorColorOptions.value.find(opt => String(opt?.value || '') === String(value || ''))
|
||||||
|
const normalizedValue = String(value || '').trim()
|
||||||
|
rowEditorForm.value.ColorCode = normalizedValue
|
||||||
|
rowEditorForm.value.sRenk = normalizedValue
|
||||||
|
if (!selected) return
|
||||||
|
rowEditorForm.value.ColorDescription = String(selected.colorDescription || '').trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRowFromEditorForm () {
|
||||||
|
const form = rowEditorForm.value
|
||||||
|
const existingRow = flatDetailRows.value.find(row => row.__rowKey === rowEditorTargetRowKey.value)
|
||||||
|
const cmPriceTypeId = normalizeCMPriceTypeId(form.cmPriceTypeChecked ? 2 : 1, form.sAciklama3)
|
||||||
|
if (!existingRow) {
|
||||||
|
newRowSequence.value += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return recalculateDetailRow({
|
||||||
|
...(existingRow || {}),
|
||||||
|
__rowKey: existingRow?.__rowKey || `new-editor-row-${newRowSequence.value}`,
|
||||||
|
isNew: Boolean(existingRow?.isNew) || rowEditorMode.value === 'new',
|
||||||
|
nOnMLNo: detailHeader.value?.nOnMLNo || onMLNo.value || '',
|
||||||
|
nStokID: String(form.nStokID || '').trim(),
|
||||||
|
sModel: String(form.sModel || '').trim(),
|
||||||
|
nOnMLDetNo: String(form.nOnMLDetNo || '').trim(),
|
||||||
|
sParcaAdi: String(form.sParcaAdi || form.sAciklama3 || '').trim(),
|
||||||
|
sAciklama3: String(form.sAciklama3 || form.sParcaAdi || 'TANIMSIZ').trim() || 'TANIMSIZ',
|
||||||
|
nHammaddeTuruNo: String(form.nHammaddeTuruNo || '').trim(),
|
||||||
|
sHammaddeTuruAdi: String(form.sHammaddeTuruAdi || '').trim(),
|
||||||
|
sKodu: String(form.sKodu || '').trim(),
|
||||||
|
sAciklama: String(form.sAciklama || '').trim(),
|
||||||
|
sRenk: String(form.sRenk || form.ColorCode || '').trim(),
|
||||||
|
ColorCode: String(form.ColorCode || form.sRenk || '').trim(),
|
||||||
|
ColorDescription: String(form.ColorDescription || '').trim(),
|
||||||
|
lMiktar: parseMoneyInput(form.miktarInput),
|
||||||
|
miktarInput: normalizeQuantityInput(form.miktarInput),
|
||||||
|
inputPrice: normalizeInputPrice(form.inputPrice),
|
||||||
|
inputPricePrBr: normalizePriceCurrency(form.inputPricePrBr) || 'USD',
|
||||||
|
fiyat_girilen: parseMoneyInput(form.inputPrice),
|
||||||
|
fiyat_doviz: normalizePriceCurrency(form.inputPricePrBr) || 'USD',
|
||||||
|
maliyeteDahil: Boolean(form.maliyeteDahil),
|
||||||
|
maliyete_dahil: form.maliyeteDahil ? 1 : 0,
|
||||||
|
Maliyete_dahil: form.maliyeteDahil ? 1 : 0,
|
||||||
|
cmPriceTypeId,
|
||||||
|
cm_price_type_id: cmPriceTypeId,
|
||||||
|
sBirim: String(form.sBirim || 'AD').trim() || 'AD',
|
||||||
|
draftChanged: true
|
||||||
|
}, {
|
||||||
|
preserveInputs: true,
|
||||||
|
priceType: 'MAN',
|
||||||
|
updateState: 'editor',
|
||||||
|
markChanged: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyEditorRowToGroups (nextRow) {
|
||||||
|
const targetGroupName = String(nextRow?.sAciklama3 || 'TANIMSIZ').trim() || 'TANIMSIZ'
|
||||||
|
const nextGroups = detailGroups.value
|
||||||
|
.map(grp => ({
|
||||||
|
...grp,
|
||||||
|
items: (Array.isArray(grp?.items) ? grp.items : []).filter(row => row.__rowKey !== nextRow.__rowKey)
|
||||||
|
}))
|
||||||
|
.filter(grp => (Array.isArray(grp?.items) ? grp.items.length : 0) > 0 || String(grp?.sAciklama3 || '').trim() === targetGroupName)
|
||||||
|
|
||||||
|
const targetIndex = nextGroups.findIndex(grp => String(grp?.sAciklama3 || '').trim() === targetGroupName)
|
||||||
|
if (targetIndex >= 0) {
|
||||||
|
nextGroups[targetIndex] = {
|
||||||
|
...nextGroups[targetIndex],
|
||||||
|
items: [...(Array.isArray(nextGroups[targetIndex].items) ? nextGroups[targetIndex].items : []), nextRow]
|
||||||
|
.sort((left, right) => parseInt(String(left?.nOnMLDetNo || '0'), 10) - parseInt(String(right?.nOnMLDetNo || '0'), 10))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nextGroups.push({
|
||||||
|
sAciklama3: targetGroupName,
|
||||||
|
totalTutar: 0,
|
||||||
|
totalUSDTutar: 0,
|
||||||
|
items: [nextRow]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
detailGroups.value = nextGroups
|
||||||
|
syncAllGroupsOpen()
|
||||||
|
schedulePersistLocalDraft()
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncAllGroupsOpen () {
|
||||||
|
const openState = {}
|
||||||
|
detailGroups.value.forEach((grp, gi) => {
|
||||||
|
openState[groupKey(grp, gi)] = true
|
||||||
|
})
|
||||||
|
groupOpenState.value = openState
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeHammaddeNo (value) {
|
||||||
|
return String(value || '').trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeGroupName (value) {
|
||||||
|
return String(value || 'TANIMSIZ').trim() || 'TANIMSIZ'
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchRequiredParcaMappings () {
|
||||||
|
const ana = String(detailHeader.value?.UrunAnaGrubu || '').trim()
|
||||||
|
const alt = String(detailHeader.value?.UrunAltGrubu || '').trim()
|
||||||
|
if (!ana || !alt) return []
|
||||||
|
|
||||||
|
const data = await get('/pricing/production-product-costing/maliyet-parca-eslestirme', {
|
||||||
|
trace_id: traceId.value,
|
||||||
|
only_active: 1,
|
||||||
|
urun_ana_grubu: ana,
|
||||||
|
urun_alt_grubu: alt
|
||||||
|
})
|
||||||
|
return Array.isArray(data) ? data : []
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureNoCostRequiredRowsFromMappings (mappings) {
|
||||||
|
const list = Array.isArray(mappings) ? mappings : []
|
||||||
|
requiredParcaMappings.value = list
|
||||||
|
if (list.length === 0) return
|
||||||
|
|
||||||
|
// Add missing placeholder rows (qty=1, price=0) to remind user
|
||||||
|
list.forEach(mapping => {
|
||||||
|
const groupName = normalizeGroupName(mapping?.parcaBolumAdi || mapping?.mtBolumAdi || mapping?.sAciklama3)
|
||||||
|
const hList = Array.isArray(mapping?.nHammaddeTurleri) ? mapping.nHammaddeTurleri : []
|
||||||
|
hList.forEach(hNoRaw => {
|
||||||
|
const hNo = normalizeHammaddeNo(hNoRaw)
|
||||||
|
if (!hNo) return
|
||||||
|
|
||||||
|
const exists = flatDetailRows.value.some(r =>
|
||||||
|
normalizeGroupName(r?.sAciklama3) === groupName &&
|
||||||
|
normalizeHammaddeNo(r?.nHammaddeTuruNo) === hNo
|
||||||
|
)
|
||||||
|
if (exists) return
|
||||||
|
|
||||||
|
newRowSequence.value += 1
|
||||||
|
const rowKey = `req-auto-row-${newRowSequence.value}`
|
||||||
|
const placeholder = recalculateDetailRow({
|
||||||
|
__rowKey: rowKey,
|
||||||
|
isNew: true,
|
||||||
|
nOnMLNo: detailHeader.value?.nOnMLNo || onMLNo.value || '',
|
||||||
|
nOnMLDetNo: '',
|
||||||
|
sParcaAdi: groupName,
|
||||||
|
sAciklama3: groupName,
|
||||||
|
nHammaddeTuruNo: hNo,
|
||||||
|
sHammaddeTuruAdi: '',
|
||||||
|
sKodu: '',
|
||||||
|
sAciklama: '',
|
||||||
|
sRenk: '',
|
||||||
|
ColorCode: '',
|
||||||
|
ColorDescription: '',
|
||||||
|
lMiktar: 1,
|
||||||
|
miktarInput: '1',
|
||||||
|
inputPrice: '0',
|
||||||
|
inputPricePrBr: 'USD',
|
||||||
|
fiyat_girilen: 0,
|
||||||
|
fiyat_doviz: 'USD',
|
||||||
|
maliyeteDahil: true,
|
||||||
|
maliyete_dahil: 1,
|
||||||
|
Maliyete_dahil: 1,
|
||||||
|
cmPriceTypeId: normalizeCMPriceTypeId(1, groupName),
|
||||||
|
cm_price_type_id: normalizeCMPriceTypeId(1, groupName),
|
||||||
|
sBirim: 'AD',
|
||||||
|
draftChanged: true,
|
||||||
|
requiredPlaceholder: true
|
||||||
|
}, {
|
||||||
|
preserveInputs: true,
|
||||||
|
priceType: 'REQ',
|
||||||
|
updateState: 'required',
|
||||||
|
markChanged: true
|
||||||
|
})
|
||||||
|
|
||||||
|
applyEditorRowToGroups(placeholder)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function computeMissingRequiredSlots () {
|
||||||
|
const list = Array.isArray(requiredParcaMappings.value) ? requiredParcaMappings.value : []
|
||||||
|
const missing = []
|
||||||
|
if (list.length === 0) return missing
|
||||||
|
|
||||||
|
list.forEach(mapping => {
|
||||||
|
const groupName = normalizeGroupName(mapping?.parcaBolumAdi || mapping?.mtBolumAdi || mapping?.sAciklama3)
|
||||||
|
const hList = Array.isArray(mapping?.nHammaddeTurleri) ? mapping.nHammaddeTurleri : []
|
||||||
|
hList.forEach(hNoRaw => {
|
||||||
|
const hNo = normalizeHammaddeNo(hNoRaw)
|
||||||
|
if (!hNo) return
|
||||||
|
|
||||||
|
const match = flatDetailRows.value.find(r =>
|
||||||
|
normalizeGroupName(r?.sAciklama3) === groupName &&
|
||||||
|
normalizeHammaddeNo(r?.nHammaddeTuruNo) === hNo
|
||||||
|
)
|
||||||
|
const price = resolveNumericRowInputPrice(match)
|
||||||
|
if (!match || !(price > 0)) {
|
||||||
|
missing.push({ groupName, nHammaddeTuruNo: hNo, rowKey: match?.__rowKey || '' })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return missing
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeDetailRowByKey (rowKey) {
|
||||||
|
const normalizedRowKey = String(rowKey || '').trim()
|
||||||
|
if (!normalizedRowKey) return false
|
||||||
|
|
||||||
|
const existingRow = flatDetailRows.value.find(r => String(r?.__rowKey || '').trim() === normalizedRowKey)
|
||||||
|
|
||||||
|
const nextGroups = detailGroups.value
|
||||||
|
.map(grp => ({
|
||||||
|
...grp,
|
||||||
|
items: (Array.isArray(grp?.items) ? grp.items : []).filter(row => row.__rowKey !== normalizedRowKey)
|
||||||
|
}))
|
||||||
|
.filter(grp => (Array.isArray(grp?.items) ? grp.items.length : 0) > 0)
|
||||||
|
|
||||||
|
const hasChanged = nextGroups.length !== detailGroups.value.length ||
|
||||||
|
nextGroups.some((grp, index) => (grp?.items?.length || 0) !== (detailGroups.value[index]?.items?.length || 0))
|
||||||
|
|
||||||
|
if (!hasChanged) return false
|
||||||
|
|
||||||
|
detailGroups.value = nextGroups
|
||||||
|
syncAllGroupsOpen()
|
||||||
|
if (existingRow && !existingRow?.isNew) {
|
||||||
|
deletedDetailRows.value = [
|
||||||
|
...(Array.isArray(deletedDetailRows.value) ? deletedDetailRows.value : []),
|
||||||
|
{
|
||||||
|
__rowKey: normalizedRowKey,
|
||||||
|
nOnMLNo: String(existingRow?.nOnMLNo || '').trim(),
|
||||||
|
nOnMLDetNo: String(existingRow?.nOnMLDetNo || '').trim(),
|
||||||
|
nHammaddeTuruNo: String(existingRow?.nHammaddeTuruNo || '').trim(),
|
||||||
|
sKodu: String(existingRow?.sKodu || '').trim(),
|
||||||
|
sRenk: String(existingRow?.sRenk || '').trim()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
schedulePersistLocalDraft()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
function openNewRowDialog () {
|
||||||
|
if (!detailHeader.value) {
|
||||||
|
$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Yeni satir eklemek icin once detay verisi olmali.',
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rowEditorMode.value = 'new'
|
||||||
|
rowEditorTargetRowKey.value = ''
|
||||||
|
rowEditorForm.value = createRowEditorForm({
|
||||||
|
isNew: true,
|
||||||
|
nOnMLDetNo: getNextDetailNo(),
|
||||||
|
inputPricePrBr: normalizePriceCurrency(detailHeader.value?.sDovizCinsi) || 'USD',
|
||||||
|
maliyeteDahil: true,
|
||||||
|
sBirim: 'AD'
|
||||||
|
})
|
||||||
|
primeRowEditorOptionsFromForm()
|
||||||
|
rowEditorDialogOpen.value = true
|
||||||
|
void bootstrapRowEditorOptions()
|
||||||
|
}
|
||||||
|
|
||||||
|
function openRowEditorForEdit (row) {
|
||||||
|
rowEditorMode.value = 'edit'
|
||||||
|
rowEditorTargetRowKey.value = String(row?.__rowKey || '').trim()
|
||||||
|
rowEditorForm.value = createRowEditorForm({
|
||||||
|
...row,
|
||||||
|
sAciklama3: row?.sAciklama3 || ''
|
||||||
|
})
|
||||||
|
primeRowEditorOptionsFromForm()
|
||||||
|
rowEditorDialogOpen.value = true
|
||||||
|
void bootstrapRowEditorOptions()
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteRowEditor () {
|
||||||
|
const targetRowKey = String(rowEditorTargetRowKey.value || rowEditorForm.value.__rowKey || '').trim()
|
||||||
|
if (!targetRowKey) return
|
||||||
|
|
||||||
|
$q.dialog({
|
||||||
|
title: 'Satiri Sil',
|
||||||
|
message: 'Bu satir gridden kaldirilacak. Devam edilsin mi?',
|
||||||
|
cancel: {
|
||||||
|
flat: true,
|
||||||
|
color: 'grey-7',
|
||||||
|
label: 'Vazgec'
|
||||||
|
},
|
||||||
|
ok: {
|
||||||
|
unelevated: true,
|
||||||
|
color: 'negative',
|
||||||
|
label: 'Sil'
|
||||||
|
},
|
||||||
|
persistent: true
|
||||||
|
}).onOk(() => {
|
||||||
|
if (!removeDetailRowByKey(targetRowKey)) {
|
||||||
|
$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Silinecek satir bulunamadi.',
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rowEditorDialogOpen.value = false
|
||||||
|
$q.notify({
|
||||||
|
type: 'positive',
|
||||||
|
message: 'Satir gridden kaldirildi.',
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveRowEditor () {
|
||||||
|
if (!String(rowEditorForm.value.nOnMLDetNo || '').trim()) {
|
||||||
|
rowEditorForm.value.nOnMLDetNo = getNextDetailNo()
|
||||||
|
}
|
||||||
|
if (!String(rowEditorForm.value.nHammaddeTuruNo || '').trim()) {
|
||||||
|
$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Hammadde Turu secilmeden satir kaydedilemez.',
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!String(rowEditorForm.value.sKodu || '').trim()) {
|
||||||
|
$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Kod secilmeden satir kaydedilemez.',
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextRow = buildRowFromEditorForm()
|
||||||
|
applyEditorRowToGroups(nextRow)
|
||||||
|
rowEditorDialogOpen.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveChanges () {
|
||||||
|
saveLoading.value = true
|
||||||
|
try {
|
||||||
|
requiredAttentionRowKeys.value = {}
|
||||||
|
if (isNoCostDetail.value) {
|
||||||
|
const missing = computeMissingRequiredSlots()
|
||||||
|
if (missing.length > 0) {
|
||||||
|
const ok = await new Promise(resolve => {
|
||||||
|
$q.dialog({
|
||||||
|
title: 'Eksik Maliyet Parcalari',
|
||||||
|
message: `Eslestirilen parcalarda (fiyat > 0) girilmemis satirlar var. Devam etmek istiyor musunuz? (Eksik: ${missing.length})`,
|
||||||
|
cancel: true,
|
||||||
|
persistent: true
|
||||||
|
}).onOk(() => resolve(true)).onCancel(() => resolve(false))
|
||||||
|
})
|
||||||
|
if (!ok) {
|
||||||
|
const next = {}
|
||||||
|
missing.forEach(x => {
|
||||||
|
if (x.rowKey) next[String(x.rowKey)] = true
|
||||||
|
})
|
||||||
|
requiredAttentionRowKeys.value = next
|
||||||
|
$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Eksik parcalar isaretlendi. Fiyat girip tekrar deneyin.',
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Kaydetme endpointi henuz eklenmedi. Buton hazir, backend baglantisi bir sonraki adimda eklenebilir.',
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
saveLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [onMLNo.value, productCode.value, recipeCode.value, detailSource.value],
|
||||||
|
() => {
|
||||||
|
fetchDetail()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => costDate.value,
|
||||||
|
value => {
|
||||||
|
const normalizedDate = normalizeDateInput(value)
|
||||||
|
if (detailHeader.value) {
|
||||||
|
detailHeader.value.dteKayitTarihi = normalizedDate
|
||||||
|
}
|
||||||
|
fetchExchangeRatesForCostDate(normalizedDate)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!canReadOrder.value) return
|
||||||
|
fetchDetail()
|
||||||
|
nextTick(() => {
|
||||||
|
updateStickyTop()
|
||||||
|
})
|
||||||
|
window.addEventListener('resize', updateStickyTop)
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
window.removeEventListener('resize', updateStickyTop)
|
||||||
|
ensureBeforeUnloadGuard(false)
|
||||||
|
if (localDraftTimer.value) window.clearTimeout(localDraftTimer.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => hasUnsavedChanges.value,
|
||||||
|
(value) => {
|
||||||
|
ensureBeforeUnloadGuard(Boolean(value))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
onBeforeRouteLeave((to, from, next) => {
|
||||||
|
if (!hasUnsavedChanges.value) {
|
||||||
|
next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
$q.dialog({
|
||||||
|
title: 'Sayfadan ayriliyorsunuz',
|
||||||
|
message: pageMode.value === 'edit'
|
||||||
|
? 'Yaptiginiz degisiklikler kaybolacak. Devam edilsin mi?'
|
||||||
|
: 'Taslak korunacak. Sayfadan cikmak istiyor musunuz?',
|
||||||
|
ok: { label: 'Evet', color: 'negative' },
|
||||||
|
cancel: { label: 'Hayir' },
|
||||||
|
persistent: true
|
||||||
|
})
|
||||||
|
.onOk(() => {
|
||||||
|
// NEW (no-cost): always persist draft so it can be resumed
|
||||||
|
if (pageMode.value === 'new') {
|
||||||
|
persistLocalDraftNow()
|
||||||
|
next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// EDIT (has-cost): allow exit, keep draft for now (later we can clear it after successful save)
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
.onCancel(() => next(false))
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => [detailLoading.value, detailError.value, !!detailHeader.value, headerInfoCollapsed.value],
|
||||||
|
() => {
|
||||||
|
nextTick(() => {
|
||||||
|
updateStickyTop()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.pcd-page {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-content-body {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-sticky-stack {
|
||||||
|
position: sticky !important;
|
||||||
|
top: 50px !important;
|
||||||
|
z-index: 1000 !important;
|
||||||
|
background: #fff;
|
||||||
|
margin-bottom: 0;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-save-toolbar {
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-toolbar-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 12px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-toolbar-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-toolbar-title {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #223048;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-toolbar-summary {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-toolbar-pill {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 5px 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #d7e0ea;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-toolbar-pill-emphasis {
|
||||||
|
background: var(--q-primary);
|
||||||
|
border-color: #145ea8;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-toolbar-pill-neutral {
|
||||||
|
background: #f5f7fa;
|
||||||
|
color: #2b3c54;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-toolbar-pill-label {
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
opacity: 0.88;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-toolbar-pill-value {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 800;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-toolbar-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 6px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-toolbar-btn {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-toolbar-btn :deep(.q-btn__content) {
|
||||||
|
min-height: 34px;
|
||||||
|
padding: 0 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-add-row-dialog {
|
||||||
|
min-width: 360px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-row-editor-dialog {
|
||||||
|
width: min(960px, 96vw);
|
||||||
|
max-width: 96vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-row-editor-dialog :deep(.pcd-row-editor-entry .q-field__control) {
|
||||||
|
background: color-mix(in srgb, var(--q-secondary) 18%, white) !important;
|
||||||
|
border: 1px solid var(--q-secondary) !important;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-row-editor-dialog :deep(.pcd-row-editor-entry .q-field__label),
|
||||||
|
.pcd-row-editor-dialog :deep(.pcd-row-editor-entry .q-field__native),
|
||||||
|
.pcd-row-editor-dialog :deep(.pcd-row-editor-entry .q-field__input),
|
||||||
|
.pcd-row-editor-dialog :deep(.pcd-row-editor-entry .q-field__marginal),
|
||||||
|
.pcd-row-editor-dialog :deep(.pcd-row-editor-entry .q-select__dropdown-icon),
|
||||||
|
.pcd-row-editor-dialog :deep(.pcd-row-editor-entry .q-field__selection) {
|
||||||
|
color: var(--q-secondary) !important;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-row-editor-dialog :deep(.pcd-row-editor-entry.q-field--focused .q-field__control) {
|
||||||
|
box-shadow: 0 0 0 1px var(--q-secondary), 0 0 0 3px color-mix(in srgb, var(--q-secondary) 24%, white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-row-editor-dialog :deep(.pcd-row-editor-entry .q-placeholder) {
|
||||||
|
color: var(--q-secondary) !important;
|
||||||
|
opacity: 0.76;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-row-editor-flag {
|
||||||
|
background: color-mix(in srgb, var(--q-secondary) 10%, white);
|
||||||
|
border: 1px solid color-mix(in srgb, var(--q-secondary) 35%, white);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 6px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-history-dialog {
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-detail-header-bar {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-emphasis-field :deep(.q-field__control) {
|
||||||
|
background: var(--q-primary) !important;
|
||||||
|
border: 1px solid #145ea8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-emphasis-field :deep(.q-field__label) {
|
||||||
|
color: #eaf3ff !important;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-emphasis-field :deep(.q-field__native),
|
||||||
|
.pcd-emphasis-field :deep(.q-field__input) {
|
||||||
|
color: #ffffff !important;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-emphasis-field-alt :deep(.q-field__control) {
|
||||||
|
background: color-mix(in srgb, var(--q-secondary) 25%, white) !important;
|
||||||
|
border: 1px solid var(--q-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-emphasis-field-alt :deep(.q-field__label) {
|
||||||
|
color: var(--q-primary) !important;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-emphasis-field-alt :deep(.q-field__native),
|
||||||
|
.pcd-emphasis-field-alt :deep(.q-field__input),
|
||||||
|
.pcd-emphasis-field-alt :deep(.q-field__selection) {
|
||||||
|
color: var(--q-primary) !important;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-readonly-header-input :deep(.q-field__control) {
|
||||||
|
background: #f8f9fa !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-group-card {
|
||||||
|
border: 1px solid #dfe3e8;
|
||||||
|
border-radius: 6px;
|
||||||
|
overflow: visible;
|
||||||
|
position: relative;
|
||||||
|
margin: 0 10px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-sub-header {
|
||||||
|
position: sticky !important;
|
||||||
|
top: var(--pcd-subheader-top) !important;
|
||||||
|
z-index: 990 !important;
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
min-height: 42px;
|
||||||
|
height: 42px;
|
||||||
|
border-top: 1px solid #d6c06a;
|
||||||
|
border-bottom: 1px solid #d6c06a;
|
||||||
|
background: linear-gradient(90deg, #fffbe9 0%, #fff4c4 50%, #fff1b0 100%);
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-sub-header .sub-left {
|
||||||
|
font-weight: 800;
|
||||||
|
color: #2b1f05;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-sub-header .sub-right {
|
||||||
|
font-weight: 900;
|
||||||
|
color: #3b2f09;
|
||||||
|
font-size: 12px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.pcd-sub-right-clickable {
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-detail-table :deep(.q-table__middle) {
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-detail-table :deep(.q-table thead) {
|
||||||
|
display: table-header-group !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-detail-table :deep(.q-table thead tr:first-child th) {
|
||||||
|
position: sticky !important;
|
||||||
|
top: calc(var(--pcd-subheader-top) + 42px) !important;
|
||||||
|
z-index: 980 !important;
|
||||||
|
background: #f8f9fa !important;
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-detail-table :deep(.q-table thead th) {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 3px 4px;
|
||||||
|
white-space: normal;
|
||||||
|
line-height: 1.2;
|
||||||
|
vertical-align: bottom;
|
||||||
|
border-bottom: 1px solid rgba(0,0,0,0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-detail-table :deep(.q-table tbody td) {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 2px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-detail-table :deep(.q-table tbody tr) {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-detail-table :deep(.pcd-detail-row-secondary td) {
|
||||||
|
background: color-mix(in srgb, var(--q-secondary) 14%, white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-detail-table :deep(.pcd-detail-row-required td) {
|
||||||
|
background: color-mix(in srgb, #ff9800 18%, white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-detail-table :deep(.pcd-entry-header) {
|
||||||
|
background: color-mix(in srgb, var(--q-primary) 16%, #f8f9fa) !important;
|
||||||
|
color: var(--q-primary) !important;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-detail-table :deep(.pcd-secondary-header) {
|
||||||
|
background: color-mix(in srgb, var(--q-secondary) 18%, #f8f9fa) !important;
|
||||||
|
color: var(--q-secondary) !important;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-detail-table :deep(.pcd-secondary-cell) {
|
||||||
|
background: color-mix(in srgb, var(--q-secondary) 10%, white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-detail-table :deep(.pcd-entry-input .q-field__control) {
|
||||||
|
background: color-mix(in srgb, var(--q-primary) 12%, white) !important;
|
||||||
|
border: 1px solid var(--q-primary) !important;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-detail-table :deep(.pcd-entry-input .q-field__native),
|
||||||
|
.pcd-detail-table :deep(.pcd-entry-input .q-field__input),
|
||||||
|
.pcd-detail-table :deep(.pcd-entry-input .q-field__marginal),
|
||||||
|
.pcd-detail-table :deep(.pcd-entry-input .q-select__dropdown-icon) {
|
||||||
|
color: var(--q-primary) !important;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-detail-table :deep(.pcd-entry-input.q-field--focused .q-field__control) {
|
||||||
|
box-shadow: 0 0 0 1px var(--q-primary), 0 0 0 3px color-mix(in srgb, var(--q-primary) 28%, white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-detail-table :deep(.pcd-entry-input .q-placeholder) {
|
||||||
|
color: var(--q-primary) !important;
|
||||||
|
opacity: 0.72;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-detail-table :deep(.pcd-inline-input .q-field__control) {
|
||||||
|
min-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-detail-table :deep(.pcd-inline-input .q-field__native),
|
||||||
|
.pcd-detail-table :deep(.pcd-inline-input .q-field__input) {
|
||||||
|
padding-top: 0;
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-detail-table {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-part-summary-card {
|
||||||
|
margin-top: 8px;
|
||||||
|
border: 1px solid #d7e0ea;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fcfdfe;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
.pcd-part-summary-title {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #2b3c54;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
padding-left: 4px;
|
||||||
|
border-left: 3px solid var(--q-primary);
|
||||||
|
}
|
||||||
|
.pcd-part-summary-table {
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
.pcd-part-summary-table thead th {
|
||||||
|
background: #f5f7fa;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #44556c;
|
||||||
|
}
|
||||||
|
.pcd-part-summary-table tbody td {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-history-table :deep(.q-table thead th) {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-history-table :deep(.q-table tbody td) {
|
||||||
|
font-size: 12px;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-history-table :deep(.pcd-history-row-purchase td) {
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-history-table :deep(.pcd-history-row-recipe td) {
|
||||||
|
background: color-mix(in srgb, var(--q-secondary) 10%, white);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-history-table :deep(.pcd-history-row-similar td) {
|
||||||
|
background: #fff8e1 !important;
|
||||||
|
border-bottom: 1px solid #ffe082 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-history-source-chip {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-width: 72px;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 999px;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-history-source-chip--purchase {
|
||||||
|
background: color-mix(in srgb, var(--q-primary) 12%, white);
|
||||||
|
color: var(--q-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-history-source-chip--bnz-v3,
|
||||||
|
.pcd-history-source-chip--bnz-rec {
|
||||||
|
background: #f3e5f5;
|
||||||
|
color: #7b1fa2;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-history-source-chip--recipe {
|
||||||
|
background: color-mix(in srgb, var(--q-secondary) 18%, white);
|
||||||
|
color: color-mix(in srgb, var(--q-secondary) 82%, black);
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcd-history-source-chip--history {
|
||||||
|
background: #eceff3;
|
||||||
|
color: #44556c;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
259
ui/src/pages/ProductionProductCostingHasCostHistory.vue
Normal file
259
ui/src/pages/ProductionProductCostingHasCostHistory.vue
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
<template>
|
||||||
|
<q-page v-if="canReadOrder" class="pch-page">
|
||||||
|
<div class="sticky-stack pch-sticky-stack">
|
||||||
|
<div class="ol-filter-bar filter-bar pch-filter-bar">
|
||||||
|
<div class="row items-center q-col-gutter-md">
|
||||||
|
<div class="col-12 col-md-4">
|
||||||
|
<q-input
|
||||||
|
:model-value="productCode || '-'"
|
||||||
|
label="Urun Kodu"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-8">
|
||||||
|
<q-input
|
||||||
|
:model-value="rows.length"
|
||||||
|
label="Toplam Maliyet Kaydi"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="save-toolbar pch-save-toolbar">
|
||||||
|
<div class="text-subtitle2 text-weight-bold">Secili Urunun Tum Maliyetleri</div>
|
||||||
|
<div class="row items-center q-gutter-sm">
|
||||||
|
<q-btn
|
||||||
|
icon="arrow_back"
|
||||||
|
label="Geri"
|
||||||
|
flat
|
||||||
|
color="grey-8"
|
||||||
|
@click="goBack"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
label="Yenile"
|
||||||
|
icon="refresh"
|
||||||
|
color="primary"
|
||||||
|
:loading="loading"
|
||||||
|
@click="fetchRows"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<q-table
|
||||||
|
title=""
|
||||||
|
class="ol-table pch-table"
|
||||||
|
flat
|
||||||
|
bordered
|
||||||
|
dense
|
||||||
|
separator="cell"
|
||||||
|
row-key="__rowKey"
|
||||||
|
:rows="rows"
|
||||||
|
:columns="columns"
|
||||||
|
:loading="loading"
|
||||||
|
no-data-label="Kayit bulunamadi"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
hide-bottom
|
||||||
|
>
|
||||||
|
<template #body-cell="props">
|
||||||
|
<q-td v-if="props.col.name === 'open'" :props="props" class="text-center">
|
||||||
|
<q-btn
|
||||||
|
icon="open_in_new"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
@click="openRow(props.row)"
|
||||||
|
>
|
||||||
|
<q-tooltip>Ac</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
</q-td>
|
||||||
|
<q-td v-else :props="props" class="pch-wrap-col">
|
||||||
|
<div class="pch-wrap-2" :title="String(props.value ?? '')">{{ props.value }}</div>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
</q-table>
|
||||||
|
|
||||||
|
<q-banner v-if="error" class="bg-red text-white q-mt-sm">
|
||||||
|
Hata: {{ error }}
|
||||||
|
</q-banner>
|
||||||
|
</q-page>
|
||||||
|
|
||||||
|
<q-page v-else class="q-pa-md flex flex-center">
|
||||||
|
<div class="text-negative text-subtitle1">
|
||||||
|
Bu module erisim yetkiniz yok.
|
||||||
|
</div>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { usePermission } from 'src/composables/usePermission'
|
||||||
|
import { get, extractApiErrorDetail } from 'src/services/api'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const { canRead } = usePermission()
|
||||||
|
const canReadOrder = canRead('order')
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const error = ref('')
|
||||||
|
const allRows = ref([])
|
||||||
|
|
||||||
|
const productCode = computed(() => String(route.query?.urun_kodu || '').trim())
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ name: 'open', label: '', field: 'open', align: 'center', sortable: false, style: 'width:3%', headerStyle: 'width:3%' },
|
||||||
|
{ name: 'nOnMLNo', label: 'nOnMLNo', field: 'nOnMLNo', align: 'left', sortable: true, style: 'width:7%', headerStyle: 'width:7%' },
|
||||||
|
{ name: 'UrunKodu', label: 'UrunKodu', field: 'UrunKodu', align: 'left', sortable: true, style: 'width:8%', headerStyle: 'width:8%' },
|
||||||
|
{ name: 'UrunAdi', label: 'UrunAdi', field: 'UrunAdi', align: 'left', sortable: true, style: 'width:12%', headerStyle: 'width:12%' },
|
||||||
|
{ name: 'Tarihi', label: 'Tarihi', field: 'Tarihi', align: 'center', sortable: true, format: val => formatDateTR(val), style: 'width:8%', headerStyle: 'width:8%' },
|
||||||
|
{ name: 'sKullaniciAdi', label: 'sKullaniciAdi', field: 'sKullaniciAdi', align: 'left', sortable: true, style: 'width:8%', headerStyle: 'width:8%' },
|
||||||
|
{ name: 'lTutarUSD', label: 'lTutarUSD', field: 'lTutarUSD', align: 'right', sortable: true, format: val => formatMoney(val), style: 'width:7%', headerStyle: 'width:7%' },
|
||||||
|
{ name: 'lTutarTL', label: 'lTutarTL', field: 'lTutarTL', align: 'right', sortable: true, format: val => formatMoney(val), style: 'width:7%', headerStyle: 'width:7%' },
|
||||||
|
{ name: 'lTutarEURO', label: 'lTutarEURO', field: 'lTutarEURO', align: 'right', sortable: true, format: val => formatMoney(val), style: 'width:7%', headerStyle: 'width:7%' },
|
||||||
|
{ name: 'sDovizCinsi', label: 'sDovizCinsi', field: 'sDovizCinsi', align: 'left', sortable: true, style: 'width:7%', headerStyle: 'width:7%' },
|
||||||
|
{ name: 'lTutarDoviz', label: 'lTutarDoviz', field: 'lTutarDoviz', align: 'right', sortable: true, format: val => formatMoney(val), style: 'width:7%', headerStyle: 'width:7%' },
|
||||||
|
{ name: 'dteGuncellemeTarihi', label: 'dteGuncellemeTarihi', field: 'dteGuncellemeTarihi', align: 'center', sortable: true, format: val => formatDateTR(val), style: 'width:9%', headerStyle: 'width:9%' },
|
||||||
|
{ name: 'sGuncellemeKullaniciAdi', label: 'sGuncellemeKullaniciAdi', field: 'sGuncellemeKullaniciAdi', align: 'left', sortable: true, style: 'width:9%', headerStyle: 'width:9%' },
|
||||||
|
{ name: 'nUrtReceteID', label: 'nUrtReceteID', field: 'nUrtReceteID', align: 'left', sortable: true, style: 'width:7%', headerStyle: 'width:7%' },
|
||||||
|
{ name: 'sAciklama', label: 'sAciklama', field: 'sAciklama', align: 'left', sortable: true, style: 'width:10%', headerStyle: 'width:10%' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const rows = computed(() => allRows.value)
|
||||||
|
|
||||||
|
function formatDateTR (value) {
|
||||||
|
const s = String(value || '').trim()
|
||||||
|
if (!s) return ''
|
||||||
|
const m = /^(\d{4})-(\d{2})-(\d{2})(?:\s+(\d{2}):(\d{2}))?/.exec(s)
|
||||||
|
if (!m) return s
|
||||||
|
const datePart = `${m[3]}.${m[2]}.${m[1]}`
|
||||||
|
if (m[4] && m[5]) return `${datePart} ${m[4]}:${m[5]}`
|
||||||
|
return datePart
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatMoney (value) {
|
||||||
|
return Number(value || 0).toLocaleString('tr-TR', {
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 4
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchRows () {
|
||||||
|
const urunKodu = productCode.value
|
||||||
|
if (!urunKodu) {
|
||||||
|
error.value = 'Urun kodu bulunamadi'
|
||||||
|
allRows.value = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
error.value = ''
|
||||||
|
try {
|
||||||
|
const data = await get('/pricing/production-product-costing/has-cost-history', {
|
||||||
|
urun_kodu: urunKodu
|
||||||
|
})
|
||||||
|
|
||||||
|
const list = Array.isArray(data) ? data : []
|
||||||
|
allRows.value = list.map((x, i) => ({
|
||||||
|
__rowKey: `${x?.nOnMLNo || ''}-${x?.UrunKodu || ''}-${i}`,
|
||||||
|
...x
|
||||||
|
}))
|
||||||
|
} catch (err) {
|
||||||
|
error.value = await extractApiErrorDetail(err)
|
||||||
|
allRows.value = []
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function goBack () {
|
||||||
|
router.push({ name: 'production-product-costing-has-cost' })
|
||||||
|
}
|
||||||
|
|
||||||
|
function openRow (row) {
|
||||||
|
const onMLNo = String(row?.nOnMLNo || '').trim()
|
||||||
|
if (!onMLNo) return
|
||||||
|
|
||||||
|
router.push({
|
||||||
|
name: 'production-product-costing-has-cost-detail',
|
||||||
|
query: {
|
||||||
|
n_onml_no: onMLNo,
|
||||||
|
urun_kodu: productCode.value || String(row?.UrunKodu || '').trim()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => productCode.value,
|
||||||
|
() => {
|
||||||
|
fetchRows()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!canReadOrder.value) return
|
||||||
|
fetchRows()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.pch-page {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pch-sticky-stack {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pch-filter-bar {
|
||||||
|
min-height: auto !important;
|
||||||
|
padding-top: 8px !important;
|
||||||
|
padding-bottom: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pch-save-toolbar {
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pch-table :deep(.q-table thead th) {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 3px 4px;
|
||||||
|
white-space: normal !important;
|
||||||
|
vertical-align: top !important;
|
||||||
|
line-height: 1.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pch-table :deep(.q-table tbody td) {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 2px 4px;
|
||||||
|
white-space: normal !important;
|
||||||
|
vertical-align: top !important;
|
||||||
|
line-height: 1.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pch-table :deep(.q-table) {
|
||||||
|
width: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pch-wrap-col {
|
||||||
|
white-space: normal !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pch-wrap-2 {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
overflow: hidden;
|
||||||
|
line-height: 1.15;
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
744
ui/src/pages/ProductionProductCostingMTBolumMapping.vue
Normal file
744
ui/src/pages/ProductionProductCostingMTBolumMapping.vue
Normal file
@@ -0,0 +1,744 @@
|
|||||||
|
<template>
|
||||||
|
<q-page v-if="canReadOrder" class="pcmm-page q-pa-md">
|
||||||
|
<div class="pcmm-header row items-center q-col-gutter-md">
|
||||||
|
<div class="col">
|
||||||
|
<div class="text-h6">Maliyet Parca Eslestirme</div>
|
||||||
|
<div class="text-caption text-grey-7">
|
||||||
|
V3 Urun Ilk Grubu (42. ozellik) + Urun Ana/Alt Grup + URETIM Parca Bolum + Hammadde Turleri eslestirmesi (URETIM mk_ tablolarinda tutulur)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-auto">
|
||||||
|
<q-btn
|
||||||
|
color="primary"
|
||||||
|
icon="refresh"
|
||||||
|
label="Yenile"
|
||||||
|
:loading="loading"
|
||||||
|
@click="refreshAll"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<q-separator class="q-my-md" />
|
||||||
|
|
||||||
|
<q-table
|
||||||
|
class="ol-table pcmm-table"
|
||||||
|
flat
|
||||||
|
bordered
|
||||||
|
dense
|
||||||
|
separator="cell"
|
||||||
|
row-key="__key"
|
||||||
|
:rows="mappings"
|
||||||
|
:columns="columns"
|
||||||
|
:loading="loading"
|
||||||
|
no-data-label="Kayit bulunamadi"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
hide-bottom
|
||||||
|
>
|
||||||
|
<template #top-right>
|
||||||
|
<div class="row items-center q-gutter-sm">
|
||||||
|
<q-input
|
||||||
|
v-model="filters.search"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
clearable
|
||||||
|
debounce="250"
|
||||||
|
label="Ara (Ana/Alt)"
|
||||||
|
style="min-width: 220px"
|
||||||
|
@update:model-value="fetchSheet"
|
||||||
|
@clear="onSearchCleared"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
color="secondary"
|
||||||
|
icon="content_copy"
|
||||||
|
label="Kopyala"
|
||||||
|
:disable="loading || saving || !canCopySelected"
|
||||||
|
@click="copySelectedToSelected"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
color="secondary"
|
||||||
|
icon="save"
|
||||||
|
label="Secilenleri Kaydet"
|
||||||
|
:loading="saving"
|
||||||
|
:disable="loading || saving || !canSaveSelected"
|
||||||
|
@click="saveSelected"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
color="primary"
|
||||||
|
icon="save"
|
||||||
|
label="Degisenleri Kaydet"
|
||||||
|
:loading="saving"
|
||||||
|
:disable="loading || saving || dirtyCount === 0"
|
||||||
|
@click="saveAll"
|
||||||
|
/>
|
||||||
|
<div class="text-caption text-grey-7 q-pl-sm">
|
||||||
|
Satir: {{ mappings.length }} | Degisen: {{ dirtyCount }} | Kopya: {{ copySelectedCount }} | Secili: {{ saveSelectedCount }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-copy_select="props">
|
||||||
|
<q-td :props="props" style="width: 54px">
|
||||||
|
<div class="row items-center no-wrap">
|
||||||
|
<q-checkbox
|
||||||
|
:model-value="isCopySelected(props.row.__key)"
|
||||||
|
@update:model-value="(v) => toggleCopySelected(props.row.__key, v)"
|
||||||
|
/>
|
||||||
|
<q-badge
|
||||||
|
v-if="copyRoleLabel(props.row.__key)"
|
||||||
|
color="grey-8"
|
||||||
|
class="q-ml-xs"
|
||||||
|
style="max-width: 110px"
|
||||||
|
>
|
||||||
|
{{ copyRoleLabel(props.row.__key) }}
|
||||||
|
<q-tooltip anchor="center left" self="center right">
|
||||||
|
{{ copyRoleLabel(props.row.__key) }}
|
||||||
|
</q-tooltip>
|
||||||
|
</q-badge>
|
||||||
|
</div>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-save_select="props">
|
||||||
|
<q-td :props="props" style="width: 54px">
|
||||||
|
<q-checkbox
|
||||||
|
:model-value="isSaveSelected(props.row.__key)"
|
||||||
|
@update:model-value="(v) => toggleSaveSelected(props.row.__key, v)"
|
||||||
|
/>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-parcaBolumAdi="props">
|
||||||
|
<q-td :props="props">
|
||||||
|
<q-select
|
||||||
|
:model-value="bolumByKey[props.row.__key] || []"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
multiple
|
||||||
|
use-chips
|
||||||
|
clearable
|
||||||
|
use-input
|
||||||
|
input-debounce="0"
|
||||||
|
:options="mtBolumOptions"
|
||||||
|
option-label="label"
|
||||||
|
option-value="value"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
class="pcmm-multi-select"
|
||||||
|
behavior="menu"
|
||||||
|
@filter="onFilterMTBolum"
|
||||||
|
@update:model-value="(val) => { updateBolumSelection(props.row.__key, val); markDirty(props.row) }"
|
||||||
|
style="min-width: 260px"
|
||||||
|
>
|
||||||
|
<template #before-options>
|
||||||
|
<q-item clickable @click="selectAllMTBolum(props.row.__key)">
|
||||||
|
<q-item-section>Tumunu Sec</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item clickable @click="clearMTBolum(props.row.__key)">
|
||||||
|
<q-item-section>Temizle</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-separator />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #selected-item="scope">
|
||||||
|
<q-chip
|
||||||
|
class="q-mr-xs"
|
||||||
|
dense
|
||||||
|
removable
|
||||||
|
@remove="scope.removeAtIndex(scope.index)"
|
||||||
|
>
|
||||||
|
{{ scope.opt.label }}
|
||||||
|
</q-chip>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #option="scope">
|
||||||
|
<q-item v-bind="scope.itemProps">
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-checkbox
|
||||||
|
:model-value="scope.selected"
|
||||||
|
tabindex="-1"
|
||||||
|
@update:model-value="() => scope.toggleOption(scope.opt)"
|
||||||
|
@click.stop
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</template>
|
||||||
|
</q-select>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-nHammaddeTurleri="props">
|
||||||
|
<q-td :props="props">
|
||||||
|
<q-select
|
||||||
|
:model-value="hammaddeByKey[props.row.__key] || []"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
multiple
|
||||||
|
use-chips
|
||||||
|
clearable
|
||||||
|
use-input
|
||||||
|
input-debounce="0"
|
||||||
|
:options="hammaddeOptions"
|
||||||
|
option-label="label"
|
||||||
|
option-value="value"
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
class="pcmm-multi-select"
|
||||||
|
behavior="menu"
|
||||||
|
@filter="onFilterHammadde"
|
||||||
|
@update:model-value="(val) => { updateHammaddeSelection(props.row.__key, val); markDirty(props.row) }"
|
||||||
|
style="min-width: 320px"
|
||||||
|
>
|
||||||
|
<template #before-options>
|
||||||
|
<q-item clickable @click="selectAllHammadde(props.row.__key)">
|
||||||
|
<q-item-section>Tumunu Sec</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-item clickable @click="clearHammadde(props.row.__key)">
|
||||||
|
<q-item-section>Temizle</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
<q-separator />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #selected-item="scope">
|
||||||
|
<q-chip
|
||||||
|
class="q-mr-xs"
|
||||||
|
dense
|
||||||
|
removable
|
||||||
|
@remove="scope.removeAtIndex(scope.index)"
|
||||||
|
>
|
||||||
|
{{ scope.opt.label }}
|
||||||
|
</q-chip>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #option="scope">
|
||||||
|
<q-item v-bind="scope.itemProps">
|
||||||
|
<q-item-section avatar>
|
||||||
|
<q-checkbox
|
||||||
|
:model-value="scope.selected"
|
||||||
|
tabindex="-1"
|
||||||
|
@update:model-value="() => scope.toggleOption(scope.opt)"
|
||||||
|
@click.stop
|
||||||
|
/>
|
||||||
|
</q-item-section>
|
||||||
|
<q-item-section>
|
||||||
|
<q-item-label>{{ scope.opt.label }}</q-item-label>
|
||||||
|
</q-item-section>
|
||||||
|
</q-item>
|
||||||
|
</template>
|
||||||
|
</q-select>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
</q-table>
|
||||||
|
</q-page>
|
||||||
|
|
||||||
|
<q-page v-else class="q-pa-md flex flex-center">
|
||||||
|
<div class="text-negative text-subtitle1">
|
||||||
|
Bu module erisim yetkiniz yok.
|
||||||
|
</div>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, onMounted, ref } from 'vue'
|
||||||
|
import { useQuasar } from 'quasar'
|
||||||
|
import { get, post, del, extractApiErrorDetail } from 'src/services/api'
|
||||||
|
import { usePermission } from 'src/composables/usePermission'
|
||||||
|
import { slog } from 'src/utils/slog'
|
||||||
|
|
||||||
|
const $q = useQuasar()
|
||||||
|
const { canRead } = usePermission()
|
||||||
|
const canReadOrder = canRead('order')
|
||||||
|
|
||||||
|
const traceId = `pcd-mtbolum-map-${crypto?.randomUUID?.() || String(Date.now())}`
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const saving = ref(false)
|
||||||
|
|
||||||
|
const mappings = ref([])
|
||||||
|
const copySelectedKeys = ref([]) // ordered
|
||||||
|
const saveSelectedKeyMap = ref({}) // key -> true
|
||||||
|
|
||||||
|
const filters = ref({
|
||||||
|
search: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const mtBolumOptions = ref([])
|
||||||
|
const hammaddeOptions = ref([])
|
||||||
|
const mtBolumLoading = ref(false)
|
||||||
|
const hammaddeLoading = ref(false)
|
||||||
|
|
||||||
|
const bolumByKey = ref({})
|
||||||
|
const hammaddeByKey = ref({})
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ name: 'copy_select', label: 'Kopya', field: 'copy_select', align: 'center' },
|
||||||
|
{ name: 'save_select', label: 'Sec', field: 'save_select', align: 'center' },
|
||||||
|
{ name: 'urunIlkGrubu', label: 'Urun Ilk Grubu', field: 'urunIlkGrubu', align: 'left', sortable: true },
|
||||||
|
{ name: 'urunAnaGrubu', label: 'Urun Ana Grubu', field: 'urunAnaGrubu', align: 'left', sortable: true },
|
||||||
|
{ name: 'urunAltGrubu', label: 'Urun Alt Grubu', field: 'urunAltGrubu', align: 'left', sortable: true },
|
||||||
|
{ name: 'parcaBolumAdi', label: 'Parca Bolum', field: row => (Array.isArray(row?.nUrtMTBolumIDs) ? row.nUrtMTBolumIDs.join(', ') : ''), align: 'left' },
|
||||||
|
{ name: 'nHammaddeTurleri', label: 'Hammadde Turleri', field: row => (Array.isArray(row?.nHammaddeTurleri) ? row.nHammaddeTurleri.join(', ') : ''), align: 'left' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const dirtyMap = ref({})
|
||||||
|
const dirtyCount = computed(() => Object.keys(dirtyMap.value || {}).length)
|
||||||
|
const copySelectedCount = computed(() => (Array.isArray(copySelectedKeys.value) ? copySelectedKeys.value.length : 0))
|
||||||
|
const canCopySelected = computed(() => copySelectedCount.value >= 2)
|
||||||
|
const saveSelectedCount = computed(() => Object.keys(saveSelectedKeyMap.value || {}).length)
|
||||||
|
const canSaveSelected = computed(() => saveSelectedCount.value > 0)
|
||||||
|
|
||||||
|
function markDirty (row) {
|
||||||
|
const key = String(row?.__key || '').trim()
|
||||||
|
if (!key) return
|
||||||
|
dirtyMap.value = { ...(dirtyMap.value || {}), [key]: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearDirty () {
|
||||||
|
dirtyMap.value = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCopySelected (rowKey) {
|
||||||
|
const key = String(rowKey || '').trim()
|
||||||
|
if (!key) return false
|
||||||
|
return (Array.isArray(copySelectedKeys.value) ? copySelectedKeys.value : []).includes(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleCopySelected (rowKey, enabled) {
|
||||||
|
const key = String(rowKey || '').trim()
|
||||||
|
if (!key) return
|
||||||
|
const current = Array.isArray(copySelectedKeys.value) ? [...copySelectedKeys.value] : []
|
||||||
|
const idx = current.indexOf(key)
|
||||||
|
if (enabled) {
|
||||||
|
if (idx === -1) current.push(key)
|
||||||
|
} else {
|
||||||
|
if (idx >= 0) current.splice(idx, 1)
|
||||||
|
}
|
||||||
|
copySelectedKeys.value = current
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyRoleLabel (rowKey) {
|
||||||
|
const key = String(rowKey || '').trim()
|
||||||
|
if (!key) return ''
|
||||||
|
const keys = Array.isArray(copySelectedKeys.value) ? copySelectedKeys.value : []
|
||||||
|
const idx = keys.indexOf(key)
|
||||||
|
if (idx === 0) return 'Kopyalanacak'
|
||||||
|
if (idx > 0) return 'Yapistirilacak'
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
function isSaveSelected (rowKey) {
|
||||||
|
const key = String(rowKey || '').trim()
|
||||||
|
if (!key) return false
|
||||||
|
return !!saveSelectedKeyMap.value?.[key]
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleSaveSelected (rowKey, enabled) {
|
||||||
|
const key = String(rowKey || '').trim()
|
||||||
|
if (!key) return
|
||||||
|
const next = { ...(saveSelectedKeyMap.value || {}) }
|
||||||
|
if (enabled) next[key] = true
|
||||||
|
else delete next[key]
|
||||||
|
saveSelectedKeyMap.value = next
|
||||||
|
}
|
||||||
|
|
||||||
|
function copySelectedToSelected () {
|
||||||
|
const keys = Array.isArray(copySelectedKeys.value) ? copySelectedKeys.value : []
|
||||||
|
if (keys.length < 2) return
|
||||||
|
const srcKey = String(keys[0] || '').trim()
|
||||||
|
if (!srcKey) return
|
||||||
|
|
||||||
|
const srcBolum = bolumByKey.value?.[srcKey] || []
|
||||||
|
const srcHam = hammaddeByKey.value?.[srcKey] || []
|
||||||
|
|
||||||
|
for (let i = 1; i < keys.length; i++) {
|
||||||
|
const key = String(keys[i] || '').trim()
|
||||||
|
if (!key) continue
|
||||||
|
updateBolumSelection(key, srcBolum)
|
||||||
|
updateHammaddeSelection(key, srcHam)
|
||||||
|
const row = (Array.isArray(mappings.value) ? mappings.value : []).find(r => String(r?.__key || '') === key)
|
||||||
|
if (row) markDirty(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
$q.notify({ type: 'positive', message: 'Kopyala / Yapistir tamamlandi' })
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveSelected () {
|
||||||
|
const keys = Object.keys(saveSelectedKeyMap.value || {})
|
||||||
|
await saveKeys(keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectAllMTBolum (rowKey) {
|
||||||
|
const key = String(rowKey || '').trim()
|
||||||
|
if (!key) return
|
||||||
|
const all = (Array.isArray(mtBolumOptions.value) ? mtBolumOptions.value : [])
|
||||||
|
.map(o => Number(o?.value))
|
||||||
|
.filter(n => Number.isFinite(n) && n > 0)
|
||||||
|
updateBolumSelection(key, all)
|
||||||
|
const row = (Array.isArray(mappings.value) ? mappings.value : []).find(r => String(r?.__key || '') === key)
|
||||||
|
if (row) markDirty(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearMTBolum (rowKey) {
|
||||||
|
const key = String(rowKey || '').trim()
|
||||||
|
if (!key) return
|
||||||
|
updateBolumSelection(key, [])
|
||||||
|
const row = (Array.isArray(mappings.value) ? mappings.value : []).find(r => String(r?.__key || '') === key)
|
||||||
|
if (row) markDirty(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectAllHammadde (rowKey) {
|
||||||
|
const key = String(rowKey || '').trim()
|
||||||
|
if (!key) return
|
||||||
|
const all = (Array.isArray(hammaddeOptions.value) ? hammaddeOptions.value : [])
|
||||||
|
.map(o => Number(o?.value))
|
||||||
|
.filter(n => Number.isFinite(n) && n > 0)
|
||||||
|
updateHammaddeSelection(key, all)
|
||||||
|
const row = (Array.isArray(mappings.value) ? mappings.value : []).find(r => String(r?.__key || '') === key)
|
||||||
|
if (row) markDirty(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearHammadde (rowKey) {
|
||||||
|
const key = String(rowKey || '').trim()
|
||||||
|
if (!key) return
|
||||||
|
updateHammaddeSelection(key, [])
|
||||||
|
const row = (Array.isArray(mappings.value) ? mappings.value : []).find(r => String(r?.__key || '') === key)
|
||||||
|
if (row) markDirty(row)
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeIntList (list) {
|
||||||
|
const arr = Array.isArray(list) ? list : []
|
||||||
|
const nums = arr
|
||||||
|
.map(x => Number(String(x).trim()))
|
||||||
|
.filter(n => Number.isFinite(n) && n > 0)
|
||||||
|
return Array.from(new Set(nums))
|
||||||
|
}
|
||||||
|
|
||||||
|
function initEditableStateFromRows (rows) {
|
||||||
|
const bolum = {}
|
||||||
|
const ham = {}
|
||||||
|
;(Array.isArray(rows) ? rows : []).forEach(r => {
|
||||||
|
const key = String(r?.__key || '').trim()
|
||||||
|
if (!key) return
|
||||||
|
bolum[key] = normalizeIntList(r?.nUrtMTBolumIDs || [])
|
||||||
|
ham[key] = normalizeIntList(r?.nHammaddeTurleri || [])
|
||||||
|
})
|
||||||
|
bolumByKey.value = bolum
|
||||||
|
hammaddeByKey.value = ham
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBolumSelection (key, newValue) {
|
||||||
|
const k = String(key || '').trim()
|
||||||
|
if (!k) return
|
||||||
|
bolumByKey.value = {
|
||||||
|
...(bolumByKey.value || {}),
|
||||||
|
[k]: normalizeIntList(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateHammaddeSelection (key, newValue) {
|
||||||
|
const k = String(key || '').trim()
|
||||||
|
if (!k) return
|
||||||
|
hammaddeByKey.value = {
|
||||||
|
...(hammaddeByKey.value || {}),
|
||||||
|
[k]: normalizeIntList(newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// label resolution now handled by options' `label` field + selected-item slot (see UserDetail.vue "Piyasalar").
|
||||||
|
async function fetchMappings () {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const data = await get('/pricing/production-product-costing/maliyet-parca-eslestirme', {
|
||||||
|
trace_id: traceId,
|
||||||
|
only_active: 1
|
||||||
|
})
|
||||||
|
return Array.isArray(data) ? data : []
|
||||||
|
} catch (e) {
|
||||||
|
const detail = await extractApiErrorDetail(e)
|
||||||
|
$q.notify({ type: 'negative', message: detail || 'Liste okunamadi' })
|
||||||
|
return []
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchSheet () {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
// Search changes should not carry over row-level selections from a previous sheet.
|
||||||
|
copySelectedKeys.value = []
|
||||||
|
saveSelectedKeyMap.value = {}
|
||||||
|
|
||||||
|
const [combos, existing] = await Promise.all([
|
||||||
|
get('/pricing/production-product-costing/options/urun-ana-alt-combos', {
|
||||||
|
trace_id: traceId,
|
||||||
|
search: String(filters.value.search || '').trim(),
|
||||||
|
limit: 5000
|
||||||
|
}),
|
||||||
|
fetchMappings()
|
||||||
|
])
|
||||||
|
|
||||||
|
const existingByKey = new Map()
|
||||||
|
;(Array.isArray(existing) ? existing : []).forEach(x => {
|
||||||
|
const k = `${String(x?.urunIlkGrubu || '').trim()}|${String(x?.urunAltGrubu || '').trim()}|${String(x?.urunAnaGrubu || '').trim()}`
|
||||||
|
const prev = existingByKey.get(k)
|
||||||
|
const nextList = Array.isArray(prev) ? prev : (prev ? [prev] : [])
|
||||||
|
nextList.push(x)
|
||||||
|
existingByKey.set(k, nextList)
|
||||||
|
})
|
||||||
|
|
||||||
|
const rows = (Array.isArray(combos) ? combos : []).map((c, idx) => {
|
||||||
|
const ilk = String(c?.urunIlkGrubu || '').trim()
|
||||||
|
const ana = String(c?.urunAnaGrubu || '').trim()
|
||||||
|
const alt = String(c?.urunAltGrubu || '').trim()
|
||||||
|
const k = `${ilk}|${alt}|${ana}`
|
||||||
|
const hitList = existingByKey.get(k)
|
||||||
|
const list = Array.isArray(hitList) ? hitList : []
|
||||||
|
const mtIds = list
|
||||||
|
.map(x => String(x?.nUrtMTBolumID || '').trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
const uniqMt = Array.from(new Set(mtIds))
|
||||||
|
|
||||||
|
// If multiple mappings exist, we merge hammadde types (union) for editing convenience.
|
||||||
|
const hSet = new Set()
|
||||||
|
list.forEach(x => {
|
||||||
|
if (Array.isArray(x?.nHammaddeTurleri)) {
|
||||||
|
x.nHammaddeTurleri.forEach(v => {
|
||||||
|
v = String(v || '').trim()
|
||||||
|
if (v) hSet.add(v)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return {
|
||||||
|
__key: k,
|
||||||
|
__rowIndex: idx,
|
||||||
|
__existingIDs: list.map(x => Number(x?.id || 0)).filter(n => n > 0),
|
||||||
|
urunIlkGrubu: ilk,
|
||||||
|
urunAnaGrubu: ana,
|
||||||
|
urunAltGrubu: alt,
|
||||||
|
nUrtMTBolumIDs: uniqMt,
|
||||||
|
nHammaddeTurleri: Array.from(hSet)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
mappings.value = rows
|
||||||
|
initEditableStateFromRows(rows)
|
||||||
|
clearDirty()
|
||||||
|
} catch (e) {
|
||||||
|
const detail = await extractApiErrorDetail(e)
|
||||||
|
$q.notify({ type: 'negative', message: detail || 'Carsaf yuklenemedi' })
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSearchCleared () {
|
||||||
|
filters.value.search = ''
|
||||||
|
copySelectedKeys.value = []
|
||||||
|
saveSelectedKeyMap.value = {}
|
||||||
|
clearDirty()
|
||||||
|
fetchSheet()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshAll () {
|
||||||
|
await Promise.all([
|
||||||
|
fetchMTBolumOptions(''),
|
||||||
|
fetchHammaddeOptions('')
|
||||||
|
])
|
||||||
|
await fetchSheet()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchMTBolumOptions (search) {
|
||||||
|
mtBolumLoading.value = true
|
||||||
|
try {
|
||||||
|
const data = await get('/pricing/production-product-costing/options/mtbolum', {
|
||||||
|
trace_id: traceId,
|
||||||
|
search: search || '',
|
||||||
|
limit: 200
|
||||||
|
})
|
||||||
|
mtBolumOptions.value = Array.isArray(data)
|
||||||
|
? data.map(x => ({
|
||||||
|
value: Number(x?.value ?? x?.nUrtMTBolumID ?? x?.id ?? 0),
|
||||||
|
label: (() => {
|
||||||
|
const v = Number(x?.value ?? x?.nUrtMTBolumID ?? x?.id ?? 0)
|
||||||
|
const name = String(x?.label || x?.sAdi || '').trim()
|
||||||
|
if (Number.isFinite(v) && v > 0 && name) return `${v} - ${name}`
|
||||||
|
if (Number.isFinite(v) && v > 0) return String(v)
|
||||||
|
return name
|
||||||
|
})()
|
||||||
|
}))
|
||||||
|
.filter(x => Number.isFinite(x.value) && x.value > 0)
|
||||||
|
.sort((a, b) => a.value - b.value)
|
||||||
|
: []
|
||||||
|
} finally {
|
||||||
|
mtBolumLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchHammaddeOptions (search) {
|
||||||
|
hammaddeLoading.value = true
|
||||||
|
try {
|
||||||
|
const data = await get('/pricing/production-product-costing/detail-editor-options', {
|
||||||
|
trace_id: traceId,
|
||||||
|
kind: 'hammadde',
|
||||||
|
search: search || '',
|
||||||
|
limit: 200
|
||||||
|
})
|
||||||
|
hammaddeOptions.value = Array.isArray(data)
|
||||||
|
? data.map(x => ({
|
||||||
|
value: Number(String(x?.nHammaddeTuruNo ?? x?.value ?? '').trim()),
|
||||||
|
label: (() => {
|
||||||
|
const v = Number(String(x?.nHammaddeTuruNo ?? x?.value ?? '').trim())
|
||||||
|
const name = String(x?.sHammaddeTuruAdi || x?.label || '').trim()
|
||||||
|
if (Number.isFinite(v) && v > 0 && name) return `${v} - ${name}`
|
||||||
|
if (Number.isFinite(v) && v > 0) return String(v)
|
||||||
|
return name
|
||||||
|
})()
|
||||||
|
}))
|
||||||
|
.filter(x => Number.isFinite(x.value) && x.value > 0)
|
||||||
|
.sort((a, b) => a.value - b.value)
|
||||||
|
: []
|
||||||
|
} finally {
|
||||||
|
hammaddeLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onFilterMTBolum (val, update) {
|
||||||
|
update(async () => {
|
||||||
|
await fetchMTBolumOptions(val)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function onFilterHammadde (val, update) {
|
||||||
|
update(async () => {
|
||||||
|
await fetchHammaddeOptions(val)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveAll () {
|
||||||
|
const dirtyKeys = Object.keys(dirtyMap.value || {})
|
||||||
|
await saveKeys(dirtyKeys)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveKeys (keys) {
|
||||||
|
const list = Array.isArray(keys) ? keys.map(k => String(k || '').trim()).filter(Boolean) : []
|
||||||
|
if (list.length === 0) return
|
||||||
|
|
||||||
|
saving.value = true
|
||||||
|
try {
|
||||||
|
const rowsToSave = mappings.value.filter(r => list.includes(String(r.__key || '').trim()))
|
||||||
|
for (const row of rowsToSave) {
|
||||||
|
const key = String(row.__key || '').trim()
|
||||||
|
const mtIds = normalizeIntList(bolumByKey.value?.[key] || [])
|
||||||
|
const hList = normalizeIntList(hammaddeByKey.value?.[key] || [])
|
||||||
|
|
||||||
|
// If hammadde cleared OR no bölüm selected: delete all existing mappings for this combo
|
||||||
|
const existingIDs = Array.isArray(row.__existingIDs) ? row.__existingIDs : []
|
||||||
|
if (existingIDs.length > 0 && (mtIds.length === 0 || hList.length === 0)) {
|
||||||
|
for (const id of existingIDs) {
|
||||||
|
await del('/pricing/production-product-costing/maliyet-parca-eslestirme', { trace_id: traceId, id })
|
||||||
|
}
|
||||||
|
row.__existingIDs = []
|
||||||
|
bolumByKey.value = { ...(bolumByKey.value || {}), [key]: [] }
|
||||||
|
hammaddeByKey.value = { ...(hammaddeByKey.value || {}), [key]: [] }
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mtIds.length > 0 && hList.length > 0) {
|
||||||
|
const existing = await fetchMappings()
|
||||||
|
const ilk = String(row.urunIlkGrubu || '').trim()
|
||||||
|
const ana = String(row.urunAnaGrubu || '').trim()
|
||||||
|
const alt = String(row.urunAltGrubu || '').trim()
|
||||||
|
const existingForCombo = (Array.isArray(existing) ? existing : []).filter(x =>
|
||||||
|
String(x?.urunIlkGrubu || '').trim() === ilk &&
|
||||||
|
String(x?.urunAnaGrubu || '').trim() === ana &&
|
||||||
|
String(x?.urunAltGrubu || '').trim() === alt
|
||||||
|
)
|
||||||
|
|
||||||
|
const selectedSet = new Set(mtIds.map(String))
|
||||||
|
for (const ex of existingForCombo) {
|
||||||
|
const exMt = String(ex?.nUrtMTBolumID || '').trim()
|
||||||
|
if (exMt && !selectedSet.has(exMt) && Number(ex?.id || 0) > 0) {
|
||||||
|
await del('/pricing/production-product-costing/maliyet-parca-eslestirme', { trace_id: traceId, id: Number(ex.id) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const idsAfter = []
|
||||||
|
for (const mtId of mtIds) {
|
||||||
|
const payload = {
|
||||||
|
urunIlkGrubu: ilk,
|
||||||
|
urunAnaGrubu: ana,
|
||||||
|
urunAltGrubu: alt,
|
||||||
|
nUrtMTBolumID: mtId,
|
||||||
|
nHammaddeTurleri: hList,
|
||||||
|
bAktif: true
|
||||||
|
}
|
||||||
|
const resp = await post('/pricing/production-product-costing/maliyet-parca-eslestirme/upsert', payload, { trace_id: traceId })
|
||||||
|
if (resp?.id) idsAfter.push(Number(resp.id))
|
||||||
|
}
|
||||||
|
row.__existingIDs = idsAfter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$q.notify({ type: 'positive', message: 'Degisiklikler kaydedildi' })
|
||||||
|
clearDirty()
|
||||||
|
// after saving, clear save selection to avoid accidental re-save
|
||||||
|
saveSelectedKeyMap.value = {}
|
||||||
|
await refreshAll()
|
||||||
|
} catch (e) {
|
||||||
|
const detail = await extractApiErrorDetail(e)
|
||||||
|
$q.notify({ type: 'negative', message: detail || 'Kaydetme basarisiz' })
|
||||||
|
} finally {
|
||||||
|
saving.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await Promise.all([
|
||||||
|
fetchMTBolumOptions(''),
|
||||||
|
fetchHammaddeOptions(''),
|
||||||
|
fetchSheet()
|
||||||
|
])
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.pcmm-page {
|
||||||
|
background: #fafafa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcmm-header {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcmm-form {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcmm-table {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allow multi-select chips to wrap and grow vertically (PowerBI-like) */
|
||||||
|
.pcmm-table :deep(.pcmm-multi-select .q-field__control) {
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 38px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcmm-table :deep(.pcmm-multi-select .q-field__native) {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcmm-table :deep(.pcmm-multi-select .q-select__chips) {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pcmm-table :deep(.pcmm-multi-select .q-chip) {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
516
ui/src/pages/ProductionProductCostingNoCost.vue
Normal file
516
ui/src/pages/ProductionProductCostingNoCost.vue
Normal file
@@ -0,0 +1,516 @@
|
|||||||
|
<template>
|
||||||
|
<q-page v-if="canReadOrder" class="npc-page">
|
||||||
|
<div class="ol-filter-bar npc-filter-bar">
|
||||||
|
<div class="npc-filter-row">
|
||||||
|
<q-input
|
||||||
|
v-model="filters.search"
|
||||||
|
class="npc-filter-input npc-search"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
clearable
|
||||||
|
debounce="300"
|
||||||
|
label="Arama (Model Kodu / Firma / Veren)"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<q-icon name="search" />
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
v-model="filters.fromDate"
|
||||||
|
class="npc-filter-input"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
type="date"
|
||||||
|
label="Baslangic Tarihi"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="ol-filter-actions npc-filter-actions">
|
||||||
|
<q-btn
|
||||||
|
label="Temizle"
|
||||||
|
icon="clear"
|
||||||
|
color="grey-7"
|
||||||
|
flat
|
||||||
|
:disable="loading"
|
||||||
|
@click="clearFilters"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
label="Kolon Filtreleri"
|
||||||
|
icon="filter_alt_off"
|
||||||
|
color="grey-7"
|
||||||
|
flat
|
||||||
|
:disable="loading"
|
||||||
|
@click="clearAllColumnFilters"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
label="Yenile"
|
||||||
|
icon="refresh"
|
||||||
|
color="primary"
|
||||||
|
:loading="loading"
|
||||||
|
@click="fetchRows"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<q-table
|
||||||
|
title="Maliyeti Olmayan Urunler"
|
||||||
|
class="ol-table npc-table"
|
||||||
|
flat
|
||||||
|
bordered
|
||||||
|
dense
|
||||||
|
separator="cell"
|
||||||
|
row-key="__rowKey"
|
||||||
|
:rows="rows"
|
||||||
|
:columns="columns"
|
||||||
|
:loading="loading"
|
||||||
|
no-data-label="Kayit bulunamadi"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
hide-bottom
|
||||||
|
>
|
||||||
|
<template #header-cell="props">
|
||||||
|
<q-th :props="props">
|
||||||
|
<div class="npc-header-cell">
|
||||||
|
<div class="npc-head-wrap-3">{{ props.col.label }}</div>
|
||||||
|
<q-btn
|
||||||
|
v-if="props.col.name !== 'open'"
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
size="sm"
|
||||||
|
icon="filter_alt"
|
||||||
|
:color="isColumnFilterActive(props.col.name) ? 'primary' : 'grey-6'"
|
||||||
|
>
|
||||||
|
<q-menu class="npc-filter-menu" fit>
|
||||||
|
<div class="npc-filter-menu-content">
|
||||||
|
<div class="text-caption text-weight-bold q-mb-sm">{{ props.col.label }}</div>
|
||||||
|
<q-input
|
||||||
|
v-model="getColumnFilter(props.col.name).text"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
clearable
|
||||||
|
label="Icerir"
|
||||||
|
/>
|
||||||
|
<q-select
|
||||||
|
v-model="getColumnFilter(props.col.name).selected"
|
||||||
|
class="q-mt-sm"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
multiple
|
||||||
|
use-chips
|
||||||
|
use-input
|
||||||
|
emit-value
|
||||||
|
map-options
|
||||||
|
:options="getColumnDistinctOptions(props.col.name)"
|
||||||
|
label="Deger Sec"
|
||||||
|
/>
|
||||||
|
<div class="row justify-end q-gutter-sm q-mt-sm">
|
||||||
|
<q-btn dense flat color="grey-7" label="Temizle" @click="clearColumnFilter(props.col.name)" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</q-menu>
|
||||||
|
</q-btn>
|
||||||
|
</div>
|
||||||
|
</q-th>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell="props">
|
||||||
|
<q-td v-if="props.col.name === 'open'" :props="props" class="text-center">
|
||||||
|
<q-btn
|
||||||
|
icon="open_in_new"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
@click="openRow(props.row)"
|
||||||
|
>
|
||||||
|
<q-tooltip>Ac</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
</q-td>
|
||||||
|
<q-td v-else :props="props" class="npc-wrap-col">
|
||||||
|
<div class="npc-wrap-3">{{ props.value }}</div>
|
||||||
|
<q-tooltip v-if="props.value">{{ props.value }}</q-tooltip>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
</q-table>
|
||||||
|
|
||||||
|
<q-banner v-if="error" class="bg-red text-white q-mt-sm">
|
||||||
|
Hata: {{ error }}
|
||||||
|
</q-banner>
|
||||||
|
</q-page>
|
||||||
|
|
||||||
|
<q-page v-else class="q-pa-md flex flex-center">
|
||||||
|
<div class="text-negative text-subtitle1">
|
||||||
|
Bu module erisim yetkiniz yok.
|
||||||
|
</div>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||||
|
import { useQuasar } from 'quasar'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { usePermission } from 'src/composables/usePermission'
|
||||||
|
import { get, extractApiErrorDetail } from 'src/services/api'
|
||||||
|
import { createTraceId, slog } from 'src/utils/slog'
|
||||||
|
|
||||||
|
const { canRead } = usePermission()
|
||||||
|
const canReadOrder = canRead('order')
|
||||||
|
const $q = useQuasar()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const error = ref('')
|
||||||
|
const allRows = ref([])
|
||||||
|
|
||||||
|
const filters = reactive({
|
||||||
|
search: '',
|
||||||
|
fromDate: '2025-06-01'
|
||||||
|
})
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ name: 'open', label: '', field: 'open', align: 'center', sortable: false, style: 'width:3%', headerStyle: 'width:3%' },
|
||||||
|
{
|
||||||
|
name: 'UretimSekli',
|
||||||
|
label: 'Uretim Sekli',
|
||||||
|
field: 'UretimSekli',
|
||||||
|
align: 'left',
|
||||||
|
sortable: true,
|
||||||
|
classes: 'npc-wrap-col',
|
||||||
|
headerClasses: 'npc-wrap-col',
|
||||||
|
style: 'width:12%',
|
||||||
|
headerStyle: 'width:12%'
|
||||||
|
},
|
||||||
|
{ name: 'nUrtSiparisNo', label: 'Uretim Siparis No', field: 'nUrtSiparisNo', align: 'left', sortable: true, style: 'width:7%', headerStyle: 'width:7%' },
|
||||||
|
{
|
||||||
|
name: 'dteIslemTarihi',
|
||||||
|
label: 'Islem Tarihi',
|
||||||
|
field: 'dteIslemTarihi',
|
||||||
|
align: 'center',
|
||||||
|
sortable: true,
|
||||||
|
format: val => formatDateTR(val),
|
||||||
|
style: 'width:7%',
|
||||||
|
headerStyle: 'width:7%'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'FirmaKodu',
|
||||||
|
label: 'Firma Kodu',
|
||||||
|
field: 'FirmaKodu',
|
||||||
|
align: 'left',
|
||||||
|
sortable: true,
|
||||||
|
classes: 'npc-wrap-col',
|
||||||
|
headerClasses: 'npc-wrap-col',
|
||||||
|
style: 'width:8%',
|
||||||
|
headerStyle: 'width:8%'
|
||||||
|
},
|
||||||
|
{ name: 'FirmaAdi', label: 'Firma Adi', field: 'FirmaAdi', align: 'left', sortable: true, style: 'width:10%', headerStyle: 'width:10%' },
|
||||||
|
{ name: 'SonIsEmriVeren', label: '2.Firma', field: 'SonIsEmriVeren', align: 'left', sortable: true, style: 'width:9%', headerStyle: 'width:9%' },
|
||||||
|
{
|
||||||
|
name: 'lMMiktar_G',
|
||||||
|
label: 'Miktar (G)',
|
||||||
|
field: 'lMMiktar_G',
|
||||||
|
align: 'right',
|
||||||
|
sortable: true,
|
||||||
|
format: val => Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2, maximumFractionDigits: 2 }),
|
||||||
|
style: 'width:7%',
|
||||||
|
headerStyle: 'width:7%'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'sMModelKodu',
|
||||||
|
label: 'Model Kodu',
|
||||||
|
field: 'sMModelKodu',
|
||||||
|
align: 'left',
|
||||||
|
sortable: true,
|
||||||
|
classes: 'npc-wrap-col',
|
||||||
|
headerClasses: 'npc-wrap-col',
|
||||||
|
style: 'width:8%',
|
||||||
|
headerStyle: 'width:8%'
|
||||||
|
},
|
||||||
|
{ name: 'sAdi', label: 'Model Adi', field: 'sAdi', align: 'left', sortable: true, style: 'width:9%', headerStyle: 'width:9%' },
|
||||||
|
{ name: 'sKodu', label: 'Recete Kodu', field: 'sKodu', align: 'left', sortable: true, style: 'width:7%', headerStyle: 'width:7%' },
|
||||||
|
{ name: 'sKullaniciAdi', label: 'Receteyi Acan Kullanici', field: 'sKullaniciAdi', align: 'left', sortable: true, style: 'width:7%', headerStyle: 'width:7%' },
|
||||||
|
{ name: 'sKullaniciAdiGunc', label: 'Receteyi Son Guncelleyen Kullanici', field: 'sKullaniciAdiGunc', align: 'left', sortable: true, style: 'width:6%', headerStyle: 'width:6%' }
|
||||||
|
]
|
||||||
|
|
||||||
|
const columnFilters = reactive({})
|
||||||
|
|
||||||
|
function getColumnFilter (name) {
|
||||||
|
if (!columnFilters[name]) {
|
||||||
|
columnFilters[name] = {
|
||||||
|
text: '',
|
||||||
|
selected: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return columnFilters[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDateTR (value) {
|
||||||
|
const s = String(value || '').trim()
|
||||||
|
if (!s) return ''
|
||||||
|
const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(s)
|
||||||
|
if (!m) return s
|
||||||
|
return `${m[3]}.${m[2]}.${m[1]}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const rows = computed(() => {
|
||||||
|
let result = allRows.value
|
||||||
|
|
||||||
|
for (const col of columns) {
|
||||||
|
if (col.name === 'open') continue
|
||||||
|
|
||||||
|
const cf = getColumnFilter(col.name)
|
||||||
|
const text = String(cf.text || '').trim().toLowerCase()
|
||||||
|
const selected = Array.isArray(cf.selected) ? cf.selected : []
|
||||||
|
|
||||||
|
if (!text && selected.length === 0) continue
|
||||||
|
|
||||||
|
result = result.filter((row) => {
|
||||||
|
const value = getColumnComparableValue(row, col.name)
|
||||||
|
const valueLC = value.toLowerCase()
|
||||||
|
|
||||||
|
if (text && !valueLC.includes(text)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selected.length > 0 && !selected.includes(value)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
|
||||||
|
function getColumnComparableValue (row, colName) {
|
||||||
|
if (colName === 'dteIslemTarihi') {
|
||||||
|
return formatDateTR(row?.dteIslemTarihi)
|
||||||
|
}
|
||||||
|
return String(row?.[colName] ?? '').trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
function getColumnDistinctOptions (colName) {
|
||||||
|
const set = new Set()
|
||||||
|
for (const row of allRows.value) {
|
||||||
|
const val = getColumnComparableValue(row, colName)
|
||||||
|
if (val) set.add(val)
|
||||||
|
}
|
||||||
|
return Array.from(set)
|
||||||
|
.sort((a, b) => a.localeCompare(b, 'tr'))
|
||||||
|
.map(v => ({ label: v, value: v }))
|
||||||
|
}
|
||||||
|
|
||||||
|
function isColumnFilterActive (name) {
|
||||||
|
const cf = getColumnFilter(name)
|
||||||
|
return !!String(cf.text || '').trim() || (Array.isArray(cf.selected) && cf.selected.length > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearColumnFilter (name) {
|
||||||
|
const cf = getColumnFilter(name)
|
||||||
|
cf.text = ''
|
||||||
|
cf.selected = []
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearAllColumnFilters () {
|
||||||
|
for (const col of columns) {
|
||||||
|
if (col.name === 'open') continue
|
||||||
|
clearColumnFilter(col.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let searchTimer = null
|
||||||
|
watch(
|
||||||
|
() => filters.search,
|
||||||
|
() => {
|
||||||
|
clearTimeout(searchTimer)
|
||||||
|
searchTimer = setTimeout(() => {
|
||||||
|
fetchRows()
|
||||||
|
}, 400)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => filters.fromDate,
|
||||||
|
() => {
|
||||||
|
fetchRows()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
async function fetchRows () {
|
||||||
|
loading.value = true
|
||||||
|
error.value = ''
|
||||||
|
try {
|
||||||
|
const data = await get('/pricing/production-product-costing/no-cost-products', {
|
||||||
|
search: filters.search || '',
|
||||||
|
from_date: filters.fromDate || ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const list = Array.isArray(data) ? data : []
|
||||||
|
allRows.value = list.map((x, i) => ({
|
||||||
|
__rowKey: `${x?.sMModelKodu || ''}-${x?.nUrtSiparisNo || 0}-${i}`,
|
||||||
|
...x
|
||||||
|
}))
|
||||||
|
} catch (err) {
|
||||||
|
error.value = await extractApiErrorDetail(err)
|
||||||
|
allRows.value = []
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearFilters () {
|
||||||
|
filters.search = ''
|
||||||
|
filters.fromDate = '2025-06-01'
|
||||||
|
clearAllColumnFilters()
|
||||||
|
fetchRows()
|
||||||
|
}
|
||||||
|
|
||||||
|
function openRow (row) {
|
||||||
|
const productCode = String(row?.sMModelKodu || '').trim()
|
||||||
|
const recipeCode = String(row?.sKodu || '').trim()
|
||||||
|
const traceId = createTraceId('pcd-no-cost')
|
||||||
|
if (!productCode || !recipeCode) {
|
||||||
|
$q.notify({
|
||||||
|
type: 'warning',
|
||||||
|
message: 'Detay acmak icin urun ve recete kodu gerekli.',
|
||||||
|
position: 'top-right'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.info('production-product-costing.no-cost', 'navigate:detail', {
|
||||||
|
trace_id: traceId,
|
||||||
|
product_code: productCode,
|
||||||
|
recipe_code: recipeCode
|
||||||
|
})
|
||||||
|
|
||||||
|
router.push({
|
||||||
|
name: 'production-product-costing-has-cost-detail',
|
||||||
|
query: {
|
||||||
|
detail_source: 'no-cost',
|
||||||
|
urun_kodu: productCode,
|
||||||
|
recete_kodu: recipeCode,
|
||||||
|
trace_id: traceId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!canReadOrder.value) return
|
||||||
|
fetchRows()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.npc-page {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-filter-bar {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-filter-row {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-filter-input {
|
||||||
|
min-width: 118px;
|
||||||
|
width: 136px;
|
||||||
|
flex: 0 0 136px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-search {
|
||||||
|
min-width: 240px;
|
||||||
|
max-width: 420px;
|
||||||
|
flex: 1 1 360px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-filter-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-filter-menu {
|
||||||
|
min-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-filter-menu-content {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-table :deep(.q-table thead th) {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 3px 4px;
|
||||||
|
white-space: normal !important;
|
||||||
|
vertical-align: top !important;
|
||||||
|
line-height: 1.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-table :deep(.q-table tbody td) {
|
||||||
|
font-size: 11px;
|
||||||
|
padding: 2px 4px;
|
||||||
|
white-space: normal !important;
|
||||||
|
vertical-align: top !important;
|
||||||
|
line-height: 1.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-table :deep(.q-table) {
|
||||||
|
width: 100%;
|
||||||
|
table-layout: fixed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-wrap-col {
|
||||||
|
white-space: normal !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-header-cell {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 4px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-head-wrap-3 {
|
||||||
|
min-width: 0;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
overflow: hidden;
|
||||||
|
line-height: 1.15;
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-wrap-3 {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
overflow: hidden;
|
||||||
|
line-height: 1.15;
|
||||||
|
white-space: normal;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1440px) {
|
||||||
|
.npc-filter-row {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-filter-actions {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.npc-filter-input {
|
||||||
|
flex: 1 1 140px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -324,6 +324,42 @@ const routes = [
|
|||||||
component: () => import('pages/ProductPricing.vue'),
|
component: () => import('pages/ProductPricing.vue'),
|
||||||
meta: { permission: 'order:view' }
|
meta: { permission: 'order:view' }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'pricing/production-product-costing',
|
||||||
|
name: 'production-product-costing',
|
||||||
|
component: () => import('pages/ProductionProductCosting.vue'),
|
||||||
|
meta: { permission: 'order:view' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'pricing/production-product-costing/has-cost',
|
||||||
|
name: 'production-product-costing-has-cost',
|
||||||
|
component: () => import('pages/ProductionProductCostingHasCost.vue'),
|
||||||
|
meta: { permission: 'order:view' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'pricing/production-product-costing/has-cost/history',
|
||||||
|
name: 'production-product-costing-has-cost-history',
|
||||||
|
component: () => import('pages/ProductionProductCostingHasCostHistory.vue'),
|
||||||
|
meta: { permission: 'order:view' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'pricing/production-product-costing/has-cost/detail',
|
||||||
|
name: 'production-product-costing-has-cost-detail',
|
||||||
|
component: () => import('pages/ProductionProductCostingHasCostDetail.vue'),
|
||||||
|
meta: { permission: 'order:view' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'pricing/production-product-costing/no-cost',
|
||||||
|
name: 'production-product-costing-no-cost',
|
||||||
|
component: () => import('pages/ProductionProductCostingNoCost.vue'),
|
||||||
|
meta: { permission: 'order:view' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'pricing/production-product-costing/maliyet-parca-eslestirme',
|
||||||
|
name: 'production-product-costing-maliyet-parca-eslestirme',
|
||||||
|
component: () => import('pages/ProductionProductCostingMTBolumMapping.vue'),
|
||||||
|
meta: { permission: 'order:view' }
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
/* ================= PASSWORD ================= */
|
/* ================= PASSWORD ================= */
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import axios from 'axios'
|
|||||||
import qs from 'qs'
|
import qs from 'qs'
|
||||||
import { useAuthStore } from 'stores/authStore'
|
import { useAuthStore } from 'stores/authStore'
|
||||||
import { DEFAULT_LOCALE, normalizeLocale } from 'src/i18n/languages'
|
import { DEFAULT_LOCALE, normalizeLocale } from 'src/i18n/languages'
|
||||||
|
import { slog } from 'src/utils/slog'
|
||||||
|
|
||||||
const rawBaseUrl =
|
const rawBaseUrl =
|
||||||
(typeof process !== 'undefined' && process.env?.VITE_API_BASE_URL) || '/api'
|
(typeof process !== 'undefined' && process.env?.VITE_API_BASE_URL) || '/api'
|
||||||
@@ -81,6 +82,20 @@ function getRequestLocale() {
|
|||||||
return normalizeLocale(window.localStorage.getItem(LOCALE_STORAGE_KEY))
|
return normalizeLocale(window.localStorage.getItem(LOCALE_STORAGE_KEY))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractTraceIdFromConfig(config) {
|
||||||
|
const rawTraceId =
|
||||||
|
config?.params?.trace_id ||
|
||||||
|
config?.headers?.['X-Trace-ID'] ||
|
||||||
|
config?.headers?.['x-trace-id'] ||
|
||||||
|
''
|
||||||
|
|
||||||
|
return String(rawTraceId || '').trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
function shouldStructuredLogRequest(config) {
|
||||||
|
return extractTraceIdFromConfig(config) !== ''
|
||||||
|
}
|
||||||
|
|
||||||
api.interceptors.request.use((config) => {
|
api.interceptors.request.use((config) => {
|
||||||
const auth = useAuthStore()
|
const auth = useAuthStore()
|
||||||
const url = config.url || ''
|
const url = config.url || ''
|
||||||
@@ -92,6 +107,22 @@ api.interceptors.request.use((config) => {
|
|||||||
config.headers ||= {}
|
config.headers ||= {}
|
||||||
config.headers['Accept-Language'] = getRequestLocale()
|
config.headers['Accept-Language'] = getRequestLocale()
|
||||||
|
|
||||||
|
const traceId = extractTraceIdFromConfig(config)
|
||||||
|
config.__traceId = traceId
|
||||||
|
config.__startedAt = Date.now()
|
||||||
|
if (traceId) {
|
||||||
|
config.headers['X-Trace-ID'] = traceId
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldStructuredLogRequest(config)) {
|
||||||
|
slog.info('production-product-costing.api', 'request:start', {
|
||||||
|
trace_id: traceId,
|
||||||
|
method: String(config.method || 'GET').toUpperCase(),
|
||||||
|
url,
|
||||||
|
params: config.params || {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return config
|
return config
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -148,7 +179,19 @@ function clearSessionAndRedirect() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
api.interceptors.response.use(
|
api.interceptors.response.use(
|
||||||
r => r,
|
(response) => {
|
||||||
|
const requestConfig = response?.config || {}
|
||||||
|
if (shouldStructuredLogRequest(requestConfig)) {
|
||||||
|
slog.info('production-product-costing.api', 'request:ok', {
|
||||||
|
trace_id: requestConfig.__traceId || extractTraceIdFromConfig(requestConfig),
|
||||||
|
method: String(requestConfig.method || 'GET').toUpperCase(),
|
||||||
|
url: String(requestConfig.url || ''),
|
||||||
|
status: response?.status || 200,
|
||||||
|
duration_ms: Math.max(0, Date.now() - Number(requestConfig.__startedAt || Date.now()))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return response
|
||||||
|
},
|
||||||
async (error) => {
|
async (error) => {
|
||||||
const requestConfig = error?.config || {}
|
const requestConfig = error?.config || {}
|
||||||
const status = error?.response?.status
|
const status = error?.response?.status
|
||||||
@@ -169,6 +212,22 @@ api.interceptors.response.use(
|
|||||||
console.error(`API ${status || '-'} ${method} ${requestUrl}: ${detail}`)
|
console.error(`API ${status || '-'} ${method} ${requestUrl}: ${detail}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldStructuredLogRequest(requestConfig)) {
|
||||||
|
const detail = sanitizeApiErrorDetail(
|
||||||
|
await extractApiErrorDetail(error),
|
||||||
|
status
|
||||||
|
)
|
||||||
|
error.parsedMessage ||= detail
|
||||||
|
slog.error('production-product-costing.api', 'request:error', {
|
||||||
|
trace_id: requestConfig.__traceId || extractTraceIdFromConfig(requestConfig),
|
||||||
|
method: String(requestConfig.method || 'GET').toUpperCase(),
|
||||||
|
url: requestUrl,
|
||||||
|
status: status || 0,
|
||||||
|
duration_ms: Math.max(0, Date.now() - Number(requestConfig.__startedAt || Date.now())),
|
||||||
|
detail
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const shouldTryRefresh =
|
const shouldTryRefresh =
|
||||||
status === 401 &&
|
status === 401 &&
|
||||||
!requestConfig._retry &&
|
!requestConfig._retry &&
|
||||||
|
|||||||
53
ui/src/utils/slog.js
Normal file
53
ui/src/utils/slog.js
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
const isDev =
|
||||||
|
typeof process !== 'undefined' &&
|
||||||
|
Boolean(process.env?.DEV)
|
||||||
|
|
||||||
|
function emit(level, scope, message, data = {}) {
|
||||||
|
const payload = {
|
||||||
|
time: new Date().toISOString(),
|
||||||
|
level,
|
||||||
|
scope,
|
||||||
|
message,
|
||||||
|
...data
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = `[${scope}] ${message}`
|
||||||
|
|
||||||
|
if (level === 'error') {
|
||||||
|
console.error(text, payload)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (level === 'warn') {
|
||||||
|
console.warn(text, payload)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (level === 'debug' && !isDev) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.info(text, payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createTraceId(prefix = 'trace') {
|
||||||
|
const rawId =
|
||||||
|
typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
|
||||||
|
? crypto.randomUUID()
|
||||||
|
: `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`
|
||||||
|
|
||||||
|
return `${prefix}-${rawId}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export const slog = {
|
||||||
|
debug(scope, message, data = {}) {
|
||||||
|
emit('debug', scope, message, data)
|
||||||
|
},
|
||||||
|
info(scope, message, data = {}) {
|
||||||
|
emit('info', scope, message, data)
|
||||||
|
},
|
||||||
|
warn(scope, message, data = {}) {
|
||||||
|
emit('warn', scope, message, data)
|
||||||
|
},
|
||||||
|
error(scope, message, data = {}) {
|
||||||
|
emit('error', scope, message, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user