ui: update ProductPerformanceProfitability page to enhance table views and tabs management
This commit is contained in:
@@ -0,0 +1,82 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bssapp-backend/db"
|
||||||
|
"bssapp-backend/queries"
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
mode := flag.String("mode", "delta", "refresh mode: full or delta")
|
||||||
|
stage := flag.String("stage", "all", "start stage: sales, stock, price, kpi, all")
|
||||||
|
startRaw := flag.String("start", "", "start date YYYY-MM-DD")
|
||||||
|
endRaw := flag.String("end", "", "end date YYYY-MM-DD")
|
||||||
|
prefix := flag.String("prefix", "", "optional product code prefix")
|
||||||
|
mssqlTimeoutSec := flag.Int("mssql-timeout-sec", 0, "override MSSQL connection/dial timeout seconds")
|
||||||
|
skipDelete := flag.Bool("skip-delete", false, "skip deleting existing cache rows")
|
||||||
|
resumeAfter := flag.Int("resume-after", 0, "skip this many MSSQL rows before inserting")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
_ = godotenv.Load(".env", "mail.env", ".env.local")
|
||||||
|
|
||||||
|
if *mssqlTimeoutSec > 0 {
|
||||||
|
timeout := strconv.Itoa(*mssqlTimeoutSec)
|
||||||
|
_ = os.Setenv("MSSQL_CONNECTION_TIMEOUT_SEC", timeout)
|
||||||
|
_ = os.Setenv("MSSQL_DIAL_TIMEOUT_SEC", timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.ConnectMSSQL(); err != nil {
|
||||||
|
log.Fatalf("mssql connect failed: %v", err)
|
||||||
|
}
|
||||||
|
pgDB, err := db.ConnectPostgres()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("postgres connect failed: %v", err)
|
||||||
|
}
|
||||||
|
defer pgDB.Close()
|
||||||
|
|
||||||
|
startDate, err := parseDateFlag(*startRaw)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("invalid -start: %v", err)
|
||||||
|
}
|
||||||
|
endDate, err := parseDateFlag(*endRaw)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("invalid -end: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
result, err := queries.RefreshProductPerformance(ctx, pgDB, queries.ProductPerformanceRefreshRequest{
|
||||||
|
Mode: strings.TrimSpace(*mode),
|
||||||
|
Stage: strings.TrimSpace(*stage),
|
||||||
|
StartDate: startDate,
|
||||||
|
EndDate: endDate,
|
||||||
|
ProductPrefix: strings.TrimSpace(*prefix),
|
||||||
|
SkipDelete: *skipDelete,
|
||||||
|
ResumeAfter: *resumeAfter,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("refresh failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("product performance refresh done: sales=%d stock=%d kpi=%d\n",
|
||||||
|
result.SalesRows,
|
||||||
|
result.StockRows,
|
||||||
|
result.KpiRows,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDateFlag(raw string) (time.Time, error) {
|
||||||
|
raw = strings.TrimSpace(raw)
|
||||||
|
if raw == "" {
|
||||||
|
return time.Time{}, nil
|
||||||
|
}
|
||||||
|
return time.ParseInLocation("2006-01-02", raw, time.Local)
|
||||||
|
}
|
||||||
@@ -528,6 +528,11 @@ func refreshProductPerformanceStockChunked(ctx context.Context, pg *sql.DB, star
|
|||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
|
copyStmt, err := prepareProductPerformanceStockCopy(ctx, tx)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer copyStmt.Close()
|
||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var r productPerformanceStockDaily
|
var r productPerformanceStockDaily
|
||||||
@@ -545,13 +550,23 @@ func refreshProductPerformanceStockChunked(ctx context.Context, pg *sql.DB, star
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := insertProductPerformanceStock(ctx, tx, r); err != nil {
|
if _, err := copyStmt.ExecContext(ctx,
|
||||||
|
r.StockDate, r.ProductCode, r.ColorCode, r.YakaKodu, r.StockQty,
|
||||||
|
r.InQty, r.OutQty, r.KpiInQty, r.KpiOutQty, r.SalesMovementQty,
|
||||||
|
r.ProductionInQty, r.PurchaseInQty, r.ConsumptionOutQty, r.CountDiffQty,
|
||||||
|
); err != nil {
|
||||||
return count, err
|
return count, err
|
||||||
}
|
}
|
||||||
count++
|
count++
|
||||||
chunkRows++
|
chunkRows++
|
||||||
if chunkRows >= chunkSize {
|
if chunkRows >= chunkSize {
|
||||||
log.Printf("[ProductPerformanceRefresh] stock chunk commit start inserted_rows=%d scanned_rows=%d", count, seen)
|
log.Printf("[ProductPerformanceRefresh] stock chunk commit start inserted_rows=%d scanned_rows=%d", count, seen)
|
||||||
|
if _, err := copyStmt.ExecContext(ctx); err != nil {
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
if err := copyStmt.Close(); err != nil {
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
if err := tx.Commit(); err != nil {
|
if err := tx.Commit(); err != nil {
|
||||||
return count, err
|
return count, err
|
||||||
}
|
}
|
||||||
@@ -560,6 +575,10 @@ func refreshProductPerformanceStockChunked(ctx context.Context, pg *sql.DB, star
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return count, err
|
return count, err
|
||||||
}
|
}
|
||||||
|
copyStmt, err = prepareProductPerformanceStockCopy(ctx, tx)
|
||||||
|
if err != nil {
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
chunkRows = 0
|
chunkRows = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -568,11 +587,18 @@ func refreshProductPerformanceStockChunked(ctx context.Context, pg *sql.DB, star
|
|||||||
}
|
}
|
||||||
if chunkRows > 0 {
|
if chunkRows > 0 {
|
||||||
log.Printf("[ProductPerformanceRefresh] stock final chunk commit start inserted_rows=%d scanned_rows=%d", count, seen)
|
log.Printf("[ProductPerformanceRefresh] stock final chunk commit start inserted_rows=%d scanned_rows=%d", count, seen)
|
||||||
|
if _, err := copyStmt.ExecContext(ctx); err != nil {
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
|
if err := copyStmt.Close(); err != nil {
|
||||||
|
return count, err
|
||||||
|
}
|
||||||
if err := tx.Commit(); err != nil {
|
if err := tx.Commit(); err != nil {
|
||||||
return count, err
|
return count, err
|
||||||
}
|
}
|
||||||
log.Printf("[ProductPerformanceRefresh] stock final chunk commit done inserted_rows=%d scanned_rows=%d elapsed=%s", count, seen, time.Since(started).Round(time.Second))
|
log.Printf("[ProductPerformanceRefresh] stock final chunk commit done inserted_rows=%d scanned_rows=%d elapsed=%s", count, seen, time.Since(started).Round(time.Second))
|
||||||
} else {
|
} else {
|
||||||
|
_ = copyStmt.Close()
|
||||||
_ = tx.Rollback()
|
_ = tx.Rollback()
|
||||||
}
|
}
|
||||||
log.Printf("[ProductPerformanceRefresh] stock refresh done inserted_rows=%d scanned_rows=%d elapsed=%s", count, seen, time.Since(started).Round(time.Second))
|
log.Printf("[ProductPerformanceRefresh] stock refresh done inserted_rows=%d scanned_rows=%d elapsed=%s", count, seen, time.Since(started).Round(time.Second))
|
||||||
@@ -583,6 +609,26 @@ type productPerformanceStockExec interface {
|
|||||||
ExecContext(context.Context, string, ...any) (sql.Result, error)
|
ExecContext(context.Context, string, ...any) (sql.Result, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func prepareProductPerformanceStockCopy(ctx context.Context, tx *sql.Tx) (*sql.Stmt, error) {
|
||||||
|
return tx.PrepareContext(ctx, pq.CopyIn(
|
||||||
|
"mk_product_performance_stock_daily",
|
||||||
|
"stock_date",
|
||||||
|
"product_code",
|
||||||
|
"color_code",
|
||||||
|
"yaka_kodu",
|
||||||
|
"stock_qty",
|
||||||
|
"in_qty",
|
||||||
|
"out_qty",
|
||||||
|
"kpi_in_qty",
|
||||||
|
"kpi_out_qty",
|
||||||
|
"sales_movement_qty",
|
||||||
|
"production_in_qty",
|
||||||
|
"purchase_in_qty",
|
||||||
|
"consumption_out_qty",
|
||||||
|
"count_diff_qty",
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
func insertProductPerformanceStock(ctx context.Context, exec productPerformanceStockExec, r productPerformanceStockDaily) error {
|
func insertProductPerformanceStock(ctx context.Context, exec productPerformanceStockExec, r productPerformanceStockDaily) error {
|
||||||
_, err := exec.ExecContext(ctx, `
|
_, err := exec.ExecContext(ctx, `
|
||||||
INSERT INTO mk_product_performance_stock_daily (
|
INSERT INTO mk_product_performance_stock_daily (
|
||||||
|
|||||||
@@ -327,6 +327,15 @@ WITH base AS (
|
|||||||
COALESCE(NULLIF(source_text_tr, ''), translated_text, '') AS source_text_tr
|
COALESCE(NULLIF(source_text_tr, ''), translated_text, '') AS source_text_tr
|
||||||
FROM mk_translator
|
FROM mk_translator
|
||||||
WHERE COALESCE(NULLIF(source_text_tr, ''), translated_text, '') <> ''
|
WHERE COALESCE(NULLIF(source_text_tr, ''), translated_text, '') <> ''
|
||||||
|
AND status = 'approved'
|
||||||
|
AND (
|
||||||
|
COALESCE(NULLIF(provider_meta->>'source_type', ''), 'dummy') <> 'dummy'
|
||||||
|
OR is_manual = true
|
||||||
|
)
|
||||||
|
AND char_length(COALESCE(NULLIF(source_text_tr, ''), translated_text, '')) <= 180
|
||||||
|
AND COALESCE(NULLIF(source_text_tr, ''), translated_text, '') !~ '[<>{}\[\]]'
|
||||||
|
AND position(chr(96) in COALESCE(NULLIF(source_text_tr, ''), translated_text, '')) = 0
|
||||||
|
AND COALESCE(NULLIF(source_text_tr, ''), translated_text, '') !~ '(=>|\|\||&&|\?\?|/\*|\*/|\.trim\(|\.replace\(|\.map\(|\.filter\()'
|
||||||
ORDER BY t_key, CASE WHEN lang_code='tr' THEN 0 ELSE 1 END, updated_at DESC
|
ORDER BY t_key, CASE WHEN lang_code='tr' THEN 0 ELSE 1 END, updated_at DESC
|
||||||
),
|
),
|
||||||
target AS (
|
target AS (
|
||||||
@@ -335,6 +344,7 @@ target AS (
|
|||||||
COALESCE(translated_text, '') AS translated_text
|
COALESCE(translated_text, '') AS translated_text
|
||||||
FROM mk_translator
|
FROM mk_translator
|
||||||
WHERE lang_code=$1
|
WHERE lang_code=$1
|
||||||
|
AND (status = 'approved' OR is_manual = true)
|
||||||
ORDER BY t_key, is_manual DESC, updated_at DESC
|
ORDER BY t_key, is_manual DESC, updated_at DESC
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
@@ -993,10 +1003,12 @@ func collectSourceSeeds(pgDB *sql.DB, mssqlDB *sql.DB, limit int) []sourceSeed {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, row := range collectDummySeeds(limit - len(out)) {
|
if strings.EqualFold(strings.TrimSpace(os.Getenv("TRANSLATION_DUMMY_SEEDS_ENABLED")), "true") {
|
||||||
appendSeed(row)
|
for _, row := range collectDummySeeds(limit - len(out)) {
|
||||||
if len(out) >= limit {
|
appendSeed(row)
|
||||||
return out
|
if len(out) >= limit {
|
||||||
|
return out
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user