- Add Tahesab/Zarrin accounting sync module with admin panel UI - Add SMS relay driver as default provider, refactor SMS into driver pattern - Update customer services, orders, products, and catalog queries - Rebuild user-app and user-app-v2 dist assets - Add Prisma migration for accounting tables - Update docker-compose and env examples - Update parity docs (DECISIONS, ISSUES, PROGRESS) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
182 lines
11 KiB
SQL
182 lines
11 KiB
SQL
-- ============================================================================
|
|
-- پیشبررسی دوم — سه سؤال باز
|
|
-- ============================================================================
|
|
--
|
|
-- ⚠️ مثل فایل اول: فقط SELECT COUNT / SUM. صفر INSERT/UPDATE/DELETE.
|
|
-- هیچ ستون متنی در خروجی نیست.
|
|
--
|
|
-- اجرا:
|
|
-- mysql -u USER -p -h HOST -P 3305 oapi < migration-precheck-2.sql > out2.txt
|
|
--
|
|
-- مرجع: docs/DATA-MIGRATION-MAP.md §۱۰.۴ · §۱۰.۶
|
|
-- ============================================================================
|
|
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- 🔴 سؤال ۱ (مسدودکننده) — مبلغ سند مالی در کدام ستون است؟
|
|
--
|
|
-- در خروجی اول، SUM(cashes.remain) و SUM(cashes.rial_remain) هر دو NULL
|
|
-- برگشتند — یعنی در هر ۶۴۸ ردیف NULL هستند. پس مبلغ جای دیگری است.
|
|
-- بدون جواب این، LedgerEntry.amount خالی پر میشود و کل دفتر مالی
|
|
-- بیمبلغ منتقل میشود.
|
|
-- ---------------------------------------------------------------------------
|
|
SELECT '=== 1. WHERE IS THE LEDGER AMOUNT? ===' AS section;
|
|
|
|
-- هر ستون عددی cashes: چند ردیف مقدار غیرNULL و غیرصفر دارند؟
|
|
SELECT 'input' AS col, COUNT(input) AS not_null, SUM(input <> 0) AS nonzero FROM cashes WHERE deleted_at IS NULL
|
|
UNION ALL SELECT 'output', COUNT(output), SUM(output <> 0) FROM cashes WHERE deleted_at IS NULL
|
|
UNION ALL SELECT 'remain', COUNT(remain), SUM(remain <> 0) FROM cashes WHERE deleted_at IS NULL
|
|
UNION ALL SELECT 'rial_input', COUNT(rial_input), SUM(rial_input <> 0) FROM cashes WHERE deleted_at IS NULL
|
|
UNION ALL SELECT 'rial_output', COUNT(rial_output), SUM(rial_output <> 0) FROM cashes WHERE deleted_at IS NULL
|
|
UNION ALL SELECT 'rial_remain', COUNT(rial_remain), SUM(rial_remain <> 0) FROM cashes WHERE deleted_at IS NULL
|
|
UNION ALL SELECT 'value', COUNT(value), SUM(value <> 0) FROM cashes WHERE deleted_at IS NULL
|
|
UNION ALL SELECT 'original_value', COUNT(original_value), SUM(original_value <> 0) FROM cashes WHERE deleted_at IS NULL
|
|
UNION ALL SELECT 'balance', COUNT(balance), SUM(balance <> 0) FROM cashes WHERE deleted_at IS NULL
|
|
UNION ALL SELECT 'price', COUNT(price), SUM(price <> 0) FROM cashes WHERE deleted_at IS NULL
|
|
UNION ALL SELECT 'price_per_gram', COUNT(price_per_gram), SUM(price_per_gram <> 0) FROM cashes WHERE deleted_at IS NULL
|
|
UNION ALL SELECT 'discount', COUNT(discount), SUM(discount <> 0) FROM cashes WHERE deleted_at IS NULL;
|
|
|
|
-- جمع کل هر ستونی که مقدار دارد → مبنای تطبیق برای ابزار
|
|
SELECT
|
|
CAST(SUM(input) AS CHAR) AS sum_input,
|
|
CAST(SUM(output) AS CHAR) AS sum_output,
|
|
CAST(SUM(value) AS CHAR) AS sum_value,
|
|
CAST(SUM(original_value) AS CHAR) AS sum_original_value,
|
|
CAST(SUM(rial_input) AS CHAR) AS sum_rial_input,
|
|
CAST(SUM(rial_output) AS CHAR) AS sum_rial_output,
|
|
CAST(SUM(price) AS CHAR) AS sum_price
|
|
FROM cashes WHERE deleted_at IS NULL;
|
|
|
|
-- به تفکیک نوع سند: کدام نوع از کدام ستون استفاده میکند؟
|
|
-- (type: 9=سفارش 1=آبشده 2=حواله 3=چک 4=پولی)
|
|
SELECT type AS cash_type,
|
|
COUNT(*) AS n,
|
|
SUM(input IS NOT NULL AND input <> 0) AS has_input,
|
|
SUM(output IS NOT NULL AND output <> 0) AS has_output,
|
|
SUM(value IS NOT NULL AND value <> 0) AS has_value,
|
|
SUM(rial_input IS NOT NULL AND rial_input <> 0) AS has_rial_in,
|
|
SUM(rial_output IS NOT NULL AND rial_output <> 0) AS has_rial_out,
|
|
SUM(price IS NOT NULL AND price <> 0) AS has_price
|
|
FROM cashes WHERE deleted_at IS NULL
|
|
GROUP BY type ORDER BY type;
|
|
|
|
-- آیا اصلاً ردیفی هست که هیچ مبلغی نداشته باشد؟
|
|
SELECT 'rows_with_no_amount_at_all' AS metric, COUNT(*) AS n
|
|
FROM cashes WHERE deleted_at IS NULL
|
|
AND COALESCE(input,0)=0 AND COALESCE(output,0)=0
|
|
AND COALESCE(value,0)=0 AND COALESCE(rial_input,0)=0
|
|
AND COALESCE(rial_output,0)=0 AND COALESCE(price,0)=0;
|
|
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- 🔴 سؤال ۲ — موبایل تکراری: آیا قاعدهی «مسئول فروش برنده» کافی است؟
|
|
--
|
|
-- ۲۷ گروه تکراری وجود دارد. اگر در یک گروه هر دو ردیف مشتری عادی باشند
|
|
-- (type != 1)، آن قاعده جواب نمیدهد و قاعدهی پشتیبان لازم است.
|
|
-- ---------------------------------------------------------------------------
|
|
SELECT '=== 2. DUPLICATE MOBILE — is the "sales expert wins" rule enough? ===' AS section;
|
|
|
|
-- در هر گروه تکراری، چند ردیف type=1 (کارشناس) هست؟
|
|
-- experts_in_group = 1 → قاعده جواب میدهد
|
|
-- experts_in_group = 0 → هیچ کارشناسی نیست، قاعده بیاثر → پشتیبان لازم
|
|
-- experts_in_group > 1 → چند کارشناس با یک شماره، قاعده مبهم
|
|
SELECT experts_in_group, COUNT(*) AS n_groups, SUM(rows_in_group) AS n_rows
|
|
FROM (
|
|
SELECT mobile,
|
|
COUNT(*) AS rows_in_group,
|
|
SUM(type = 1) AS experts_in_group
|
|
FROM customers
|
|
WHERE deleted_at IS NULL AND mobile IS NOT NULL AND mobile <> ''
|
|
GROUP BY mobile HAVING COUNT(*) > 1
|
|
) g
|
|
GROUP BY experts_in_group ORDER BY experts_in_group;
|
|
|
|
-- آیا ردیفهای تکراری updated_at متفاوت دارند؟ (قاعدهی پشتیبان پیشنهادی)
|
|
-- اگر n_ties > 0 یعنی updated_at هم تفکیک نمیکند
|
|
SELECT 'groups_where_updated_at_ties' AS metric, COUNT(*) AS n
|
|
FROM (
|
|
SELECT mobile, COUNT(*) AS c, COUNT(DISTINCT updated_at) AS d
|
|
FROM customers
|
|
WHERE deleted_at IS NULL AND mobile IS NOT NULL AND mobile <> ''
|
|
GROUP BY mobile HAVING COUNT(*) > 1 AND COUNT(DISTINCT updated_at) < COUNT(*)
|
|
) t;
|
|
|
|
-- آیا ردیفهای تکراری بدهی دارند؟ ادغامشان یعنی جمعکردن بدهی
|
|
SELECT 'dup_groups_with_debt' AS metric, COUNT(*) AS n
|
|
FROM (
|
|
SELECT mobile FROM customers
|
|
WHERE deleted_at IS NULL AND mobile IS NOT NULL AND mobile <> ''
|
|
GROUP BY mobile
|
|
HAVING COUNT(*) > 1 AND (SUM(COALESCE(remain,0)) <> 0 OR SUM(COALESCE(rial_remain,0)) <> 0)
|
|
) t;
|
|
|
|
-- آیا ردیفهای تکراری سفارش دارند؟
|
|
SELECT 'dup_rows_with_orders' AS metric, COUNT(DISTINCT c.id) AS n
|
|
FROM customers c
|
|
JOIN orders o ON o.customer_id = c.id AND o.deleted_at IS NULL
|
|
WHERE c.deleted_at IS NULL AND c.mobile IN (
|
|
SELECT mobile FROM customers
|
|
WHERE deleted_at IS NULL AND mobile IS NOT NULL AND mobile <> ''
|
|
GROUP BY mobile HAVING COUNT(*) > 1
|
|
);
|
|
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- سؤال ۳ — نگاشت تصویر محصول: سه ستون → چهار نقش ProductImageRole
|
|
--
|
|
-- مرجع: §۹ — file_id / mobile_pic_id / indicator_id_id
|
|
-- ---------------------------------------------------------------------------
|
|
SELECT '=== 3. PRODUCT IMAGE ROLES ===' AS section;
|
|
|
|
SELECT 'file_id (FEATURED?)' AS col, COUNT(file_id) AS not_null FROM product WHERE deleted_at IS NULL
|
|
UNION ALL SELECT 'mobile_pic_id (FEATURED_MOBILE?)', COUNT(mobile_pic_id) FROM product WHERE deleted_at IS NULL
|
|
UNION ALL SELECT 'indicator_id_id (?)', COUNT(indicator_id_id) FROM product WHERE deleted_at IS NULL;
|
|
|
|
-- products_productimage همان GALLERY است؟ چند تصویر per محصول؟
|
|
SELECT 'gallery_rows' AS metric, COUNT(*) AS n FROM products_productimage WHERE deleted_at IS NULL
|
|
UNION ALL SELECT 'gallery_distinct_products', COUNT(DISTINCT product_id) FROM products_productimage WHERE deleted_at IS NULL
|
|
UNION ALL SELECT 'gallery_orphan_image', SUM(i.id IS NULL)
|
|
FROM products_productimage pi LEFT JOIN image i ON i.id = pi.image_id WHERE pi.deleted_at IS NULL
|
|
UNION ALL SELECT 'gallery_orphan_product', SUM(p.id IS NULL)
|
|
FROM products_productimage pi LEFT JOIN product p ON p.id = pi.product_id WHERE pi.deleted_at IS NULL;
|
|
|
|
-- ستون w در products_productimage — آیا نقش را تفکیک میکند؟
|
|
SELECT 'prod_image.w' AS col, COALESCE(w,'(null)') AS val, COUNT(*) AS n
|
|
FROM products_productimage WHERE deleted_at IS NULL GROUP BY w ORDER BY n DESC LIMIT 10;
|
|
|
|
-- یتیمهای تصویر محصول (file_id → image)
|
|
SELECT 'product.file_id orphan' AS ref, COUNT(*) AS orphans
|
|
FROM product p LEFT JOIN image i ON i.id = p.file_id
|
|
WHERE p.deleted_at IS NULL AND p.file_id IS NOT NULL AND i.id IS NULL
|
|
UNION ALL
|
|
SELECT 'product.mobile_pic_id orphan', COUNT(*)
|
|
FROM product p LEFT JOIN image i ON i.id = p.mobile_pic_id
|
|
WHERE p.deleted_at IS NULL AND p.mobile_pic_id IS NOT NULL AND i.id IS NULL;
|
|
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- سؤال ۴ — کارشناس فروش (type=1): ۵۶ ردیف که به AdminUser میروند
|
|
-- مرجع: §۵.۷ · D17
|
|
-- ---------------------------------------------------------------------------
|
|
SELECT '=== 4. SALES EXPERTS (type=1 -> AdminUser) ===' AS section;
|
|
|
|
SELECT 'experts_total' AS metric, COUNT(*) AS n FROM customers WHERE deleted_at IS NULL AND type = 1
|
|
UNION ALL SELECT 'experts_with_password', SUM(password IS NOT NULL AND password <> '') FROM customers WHERE deleted_at IS NULL AND type = 1
|
|
UNION ALL SELECT 'experts_with_ncode', SUM(ncode IS NOT NULL AND ncode <> '') FROM customers WHERE deleted_at IS NULL AND type = 1
|
|
UNION ALL SELECT 'experts_with_city', SUM(city_id IS NOT NULL) FROM customers WHERE deleted_at IS NULL AND type = 1
|
|
UNION ALL SELECT 'experts_with_percent', SUM(percent IS NOT NULL AND percent <> 0) FROM customers WHERE deleted_at IS NULL AND type = 1
|
|
UNION ALL SELECT 'experts_referenced_as_reagent', COUNT(DISTINCT reagent_id) FROM customers WHERE deleted_at IS NULL AND reagent_id IS NOT NULL;
|
|
|
|
-- 🔴 حیاتی: آیا reagent_id واقعاً به customers اشاره میکند؟ (§۵.۷)
|
|
-- اگر orphan_in_customers صفر یا کم باشد → تأیید میشود
|
|
SELECT 'reagent_id orphan vs customers' AS ref, COUNT(*) AS orphans
|
|
FROM customers c LEFT JOIN customers r ON r.id = c.reagent_id
|
|
WHERE c.deleted_at IS NULL AND c.reagent_id IS NOT NULL AND r.id IS NULL;
|
|
|
|
-- آیا همهی reagent_id ها به ردیف type=1 اشاره میکنند؟
|
|
SELECT 'reagent points to non-expert' AS ref, COUNT(*) AS n
|
|
FROM customers c JOIN customers r ON r.id = c.reagent_id
|
|
WHERE c.deleted_at IS NULL AND r.type <> 1;
|
|
|
|
SELECT '=== END ===' AS section;
|