Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -9,21 +9,22 @@ import (
|
||||
)
|
||||
|
||||
type ProductionCostingLast10WarningRow struct {
|
||||
NOnMLNo int `json:"n_onml_no"`
|
||||
UrunKodu string `json:"urun_kodu"`
|
||||
MaliyetTarihi string `json:"maliyet_tarihi"` // YYYY-MM-DD
|
||||
ItemCode string `json:"item_code"`
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
InputPrice float64 `json:"input_price"`
|
||||
AvgDocPrice float64 `json:"avg_doc_price"`
|
||||
InputUSD float64 `json:"input_usd"`
|
||||
AvgUSD float64 `json:"avg_usd"`
|
||||
DiffRatio float64 `json:"diff_ratio"` // e.g. 0.12 means 12%
|
||||
SampleCount int `json:"sample_count"`
|
||||
MinInvoice string `json:"min_invoice_date,omitempty"`
|
||||
MaxInvoice string `json:"max_invoice_date,omitempty"`
|
||||
CreatedAt string `json:"created_at,omitempty"`
|
||||
CreatedBy string `json:"created_by,omitempty"`
|
||||
NOnMLNo int `json:"n_onml_no"`
|
||||
UrunKodu string `json:"urun_kodu"`
|
||||
MaliyetTarihi string `json:"maliyet_tarihi"` // YYYY-MM-DD
|
||||
ItemCode string `json:"item_code"`
|
||||
ItemDescription string `json:"item_description,omitempty"`
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
InputPrice float64 `json:"input_price"`
|
||||
AvgDocPrice float64 `json:"avg_doc_price"`
|
||||
InputUSD float64 `json:"input_usd"`
|
||||
AvgUSD float64 `json:"avg_usd"`
|
||||
DiffRatio float64 `json:"diff_ratio"` // e.g. 0.12 means 12%
|
||||
SampleCount int `json:"sample_count"`
|
||||
MinInvoice string `json:"min_invoice_date,omitempty"`
|
||||
MaxInvoice string `json:"max_invoice_date,omitempty"`
|
||||
CreatedAt string `json:"created_at,omitempty"`
|
||||
CreatedBy string `json:"created_by,omitempty"`
|
||||
}
|
||||
|
||||
func EnsureProductionCostingLast10WarningTables(pg *sql.DB) error {
|
||||
@@ -37,6 +38,7 @@ CREATE TABLE IF NOT EXISTS mk_costing_last10_warning (
|
||||
item_code TEXT NOT NULL,
|
||||
currency_code TEXT NOT NULL,
|
||||
urun_kodu TEXT NOT NULL DEFAULT '',
|
||||
item_description TEXT NOT NULL DEFAULT '',
|
||||
maliyet_tarihi DATE,
|
||||
input_price DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
avg_doc_price DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||
@@ -51,6 +53,8 @@ CREATE TABLE IF NOT EXISTS mk_costing_last10_warning (
|
||||
PRIMARY KEY (n_onml_no, item_code, currency_code)
|
||||
)
|
||||
`,
|
||||
// Best-effort forward migration for existing DBs.
|
||||
`ALTER TABLE mk_costing_last10_warning ADD COLUMN IF NOT EXISTS item_description TEXT NOT NULL DEFAULT ''`,
|
||||
`CREATE INDEX IF NOT EXISTS ix_costing_last10_warning_onml ON mk_costing_last10_warning (n_onml_no)`,
|
||||
`CREATE INDEX IF NOT EXISTS ix_costing_last10_warning_item ON mk_costing_last10_warning (item_code, currency_code)`,
|
||||
}
|
||||
@@ -73,6 +77,7 @@ func ReplaceProductionCostingLast10Warnings(
|
||||
inputByKey map[string]float64, // key = ITEM|CUR (input in doc currency)
|
||||
inputUSDByKey map[string]float64, // key = ITEM|CUR (input converted to USD basis)
|
||||
avgUSDByKey map[string]float64, // key = ITEM|CUR (avg converted to USD basis)
|
||||
descByCode map[string]string, // code -> description (UI text)
|
||||
) error {
|
||||
if pg == nil {
|
||||
return fmt.Errorf("pg db is nil")
|
||||
@@ -128,22 +133,25 @@ func ReplaceProductionCostingLast10Warnings(
|
||||
continue
|
||||
}
|
||||
|
||||
desc := strings.TrimSpace(descByCode[code])
|
||||
|
||||
_, err := tx.ExecContext(ctx, `
|
||||
INSERT INTO mk_costing_last10_warning (
|
||||
n_onml_no, item_code, currency_code,
|
||||
urun_kodu, maliyet_tarihi,
|
||||
urun_kodu, item_description, maliyet_tarihi,
|
||||
input_price, avg_doc_price, input_usd, avg_usd, diff_ratio,
|
||||
sample_count, min_invoice_date, max_invoice_date,
|
||||
created_by
|
||||
) VALUES (
|
||||
$1,$2,$3,
|
||||
$4,$5,
|
||||
$6,$7,$8,$9,$10,
|
||||
$11,$12,$13,
|
||||
$14
|
||||
$4,$5,$6,
|
||||
$7,$8,$9,$10,$11,
|
||||
$12,$13,$14,
|
||||
$15
|
||||
)
|
||||
ON CONFLICT (n_onml_no, item_code, currency_code) DO UPDATE SET
|
||||
urun_kodu = EXCLUDED.urun_kodu,
|
||||
item_description = EXCLUDED.item_description,
|
||||
maliyet_tarihi = EXCLUDED.maliyet_tarihi,
|
||||
input_price = EXCLUDED.input_price,
|
||||
avg_doc_price = EXCLUDED.avg_doc_price,
|
||||
@@ -155,7 +163,7 @@ ON CONFLICT (n_onml_no, item_code, currency_code) DO UPDATE SET
|
||||
max_invoice_date = EXCLUDED.max_invoice_date,
|
||||
created_at = now(),
|
||||
created_by = EXCLUDED.created_by
|
||||
`, nOnMLNo, code, cur, urunKodu, mtDate, in, ar.AvgDocPrice, inUSD, avgUSD, diff, ar.SampleCount, ar.MinInvoiceDate, ar.MaxInvoiceDate, createdBy)
|
||||
`, nOnMLNo, code, cur, urunKodu, desc, mtDate, in, ar.AvgDocPrice, inUSD, avgUSD, diff, ar.SampleCount, ar.MinInvoiceDate, ar.MaxInvoiceDate, createdBy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -177,6 +185,7 @@ SELECT
|
||||
COALESCE(urun_kodu,'') AS urun_kodu,
|
||||
COALESCE(TO_CHAR(maliyet_tarihi, 'YYYY-MM-DD'),'') AS maliyet_tarihi,
|
||||
item_code,
|
||||
COALESCE(item_description,'') AS item_description,
|
||||
currency_code,
|
||||
input_price,
|
||||
avg_doc_price,
|
||||
@@ -205,6 +214,7 @@ ORDER BY diff_ratio DESC, item_code ASC, currency_code ASC
|
||||
&r.UrunKodu,
|
||||
&r.MaliyetTarihi,
|
||||
&r.ItemCode,
|
||||
&r.ItemDescription,
|
||||
&r.CurrencyCode,
|
||||
&r.InputPrice,
|
||||
&r.AvgDocPrice,
|
||||
|
||||
@@ -2266,6 +2266,7 @@ VALUES (
|
||||
// dedupe input by code+currency (USD basis comparison)
|
||||
inputByKey := map[string]float64{}
|
||||
inputUSDByKey := map[string]float64{}
|
||||
descByCode := map[string]string{}
|
||||
codes := make([]string, 0, len(reqCopy.Detail.Upserts))
|
||||
seenCode := map[string]struct{}{}
|
||||
|
||||
@@ -2310,6 +2311,13 @@ VALUES (
|
||||
}
|
||||
inputUSDByKey[key] = inUSD
|
||||
|
||||
// Best-effort description from UI payload (for excel export/readability).
|
||||
if d := strings.TrimSpace(row.SAciklama); d != "" {
|
||||
if _, ok := descByCode[code]; !ok {
|
||||
descByCode[code] = d
|
||||
}
|
||||
}
|
||||
|
||||
if _, ok := seenCode[code]; !ok {
|
||||
seenCode[code] = struct{}{}
|
||||
codes = append(codes, code)
|
||||
@@ -2366,6 +2374,7 @@ VALUES (
|
||||
inputByKey,
|
||||
inputUSDByKey,
|
||||
avgUSDByKey,
|
||||
descByCode,
|
||||
)
|
||||
cancelWrite()
|
||||
if err != nil {
|
||||
|
||||
@@ -279,6 +279,16 @@
|
||||
<q-card-section class="row items-center">
|
||||
<div class="text-h6">Son 10 Ort. Fiyat Sapmalari</div>
|
||||
<q-space />
|
||||
<q-btn
|
||||
dense
|
||||
outline
|
||||
color="secondary"
|
||||
icon="download"
|
||||
label="Excel"
|
||||
class="q-mr-sm"
|
||||
:disable="!last10Warnings || last10Warnings.length === 0"
|
||||
@click="downloadLast10WarningsExcel"
|
||||
/>
|
||||
<q-btn icon="close" flat round dense v-close-popup />
|
||||
</q-card-section>
|
||||
<q-separator />
|
||||
@@ -287,6 +297,7 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-left">Kod</th>
|
||||
<th class="text-left">Aciklama</th>
|
||||
<th class="text-left">Doviz</th>
|
||||
<th class="text-right">Giris</th>
|
||||
<th class="text-right">Ort10</th>
|
||||
@@ -298,6 +309,7 @@
|
||||
<tbody>
|
||||
<tr v-for="w in last10Warnings" :key="w.item_code + '|' + w.currency_code">
|
||||
<td class="text-left">{{ w.item_code }}</td>
|
||||
<td class="text-left">{{ w.item_description || '' }}</td>
|
||||
<td class="text-left">{{ w.currency_code }}</td>
|
||||
<td class="text-right">{{ formatMoney(w.input_price) }}</td>
|
||||
<td class="text-right">{{ formatMoney(w.avg_doc_price) }}</td>
|
||||
@@ -306,7 +318,7 @@
|
||||
<td class="text-left">{{ (w.min_invoice_date || '-') + ' / ' + (w.max_invoice_date || '-') }}</td>
|
||||
</tr>
|
||||
<tr v-if="last10Warnings.length === 0">
|
||||
<td colspan="7" class="text-center text-grey-7">Kayit yok</td>
|
||||
<td colspan="8" class="text-center text-grey-7">Kayit yok</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</q-markup-table>
|
||||
@@ -1155,6 +1167,71 @@ function formatPercent (ratio) {
|
||||
return `${(n * 100).toFixed(0)}%`
|
||||
}
|
||||
|
||||
function downloadLast10WarningsExcel () {
|
||||
const rows = Array.isArray(last10Warnings.value) ? last10Warnings.value : []
|
||||
if (rows.length === 0) return
|
||||
|
||||
// Excel-friendly CSV (UTF-8 with BOM).
|
||||
const header = [
|
||||
'Kod',
|
||||
'Aciklama',
|
||||
'Doviz',
|
||||
'Giris',
|
||||
'Ort10',
|
||||
'SapmaOran',
|
||||
'SapmaYuzde',
|
||||
'Sample',
|
||||
'MinTarih',
|
||||
'MaxTarih'
|
||||
]
|
||||
const lines = [header.join(';')]
|
||||
for (const w of rows) {
|
||||
const code = String(w?.item_code || '').trim()
|
||||
const desc = String(w?.item_description || '').replaceAll('\n', ' ').replaceAll('\r', ' ').trim()
|
||||
const cur = String(w?.currency_code || '').trim()
|
||||
const inP = Number(w?.input_price || 0)
|
||||
const avgP = Number(w?.avg_doc_price || 0)
|
||||
const diff = Number(w?.diff_ratio || 0)
|
||||
const sample = Number(w?.sample_count || 0)
|
||||
const minD = String(w?.min_invoice_date || '').trim()
|
||||
const maxD = String(w?.max_invoice_date || '').trim()
|
||||
|
||||
const safe = (s) => {
|
||||
s = String(s ?? '')
|
||||
if (s.includes(';') || s.includes('\"')) {
|
||||
s = '\"' + s.replaceAll('\"', '\"\"') + '\"'
|
||||
}
|
||||
return s
|
||||
}
|
||||
lines.push([
|
||||
safe(code),
|
||||
safe(desc),
|
||||
safe(cur),
|
||||
String(Number.isFinite(inP) ? inP : 0),
|
||||
String(Number.isFinite(avgP) ? avgP : 0),
|
||||
String(Number.isFinite(diff) ? diff : 0),
|
||||
`${Number.isFinite(diff) ? (diff * 100).toFixed(0) : '0'}%`,
|
||||
String(Number.isFinite(sample) ? sample : 0),
|
||||
safe(minD),
|
||||
safe(maxD)
|
||||
].join(';'))
|
||||
}
|
||||
|
||||
const bom = '\uFEFF'
|
||||
const blob = new Blob([bom + lines.join('\n')], { type: 'text/csv;charset=utf-8;' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
const urun = String(detailHeader.value?.UrunKodu || productCode.value || '').trim()
|
||||
const onml = String(onMLNo.value || '').trim()
|
||||
const date = String(costDate.value || '').trim()
|
||||
a.href = url
|
||||
a.download = `fiyat_uyari_${urun || 'urun'}_${onml || 'onml'}_${date || 'tarih'}.csv`
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
a.remove()
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
|
||||
async function refreshLast10Warnings () {
|
||||
if (!onMLNo.value) {
|
||||
last10Warnings.value = []
|
||||
|
||||
Reference in New Issue
Block a user