راهنمای جامع یکپارچهسازی و پایگاه داده Sadr Scales
مرجع کامل اتصال نرمافزارهای POS، ERP و حسابداری؛ ساختار SQL، کالا، ترازو، کلید سریع، فروش و نمونه کد.
نقشه سریع پایگاه داده
| دسترسی | کاربرد | حوزه | Object |
|---|---|---|---|
| Read / Write — قرارداد عمومی v1 | گروه کالا | Master | dbo.SADR_ItemClass |
| Read / Write کنترلشده — خارج از مسیر پایه v1 | شعبه | Master | dbo.SADR_Store |
| Read / Write کنترلشده — خارج از مسیر پایه v1 | تعریف و فعال/غیرفعالسازی ترازو | Registry | dbo.SADR_Scale |
| Replace Transactional — کنترلشده | گروههای هر ترازو | Mapping | dbo.SADR_ScaleItemClass |
| Read / Write — قرارداد عمومی v1 | کالا / PLU | Master | dbo.SADR_Item |
| Read / Write کنترلشده | کلید سریع گروهی | Mapping | dbo.SADR_KeyAssignment |
| Replace Transactional — کنترلشده | کد و کلید اختصاصی هر ترازو | Mapping | dbo.SADR_ScaleItemMap |
| Read-only — قرارداد عمومی v1 | Feed تخت فروش | Sales | dbo.SADR_Logs |
| Read + Ack کنترلشده — فروش ساختیافته | Header و ریز فاکتور | Sales | dbo.SADR_Total + SADR_Detail |
| Do not write — داخلی | Semantic Sync | Internal | dbo.SADR_ItemSyncState |
برای غیرفعالسازی فقط Used=0 بگذارید.
DELETE فیزیکی Scale میتواند فروشها و Mappingهای وابسته را نیز حذف
کند.
| فروش: SADR_Logs برای Feed ساده است؛ SADR_Total + SADR_Detail برای Header/Detail و اسکن TotalBarcode. در قرارداد، LableStatus پس از ثبت موفق میتواند 1 شود. |
|---|
۱. هدف و محدوده سند
این سند مرجع کامل اتصال مستقیم نرمافزارهای ثالث به پایگاه داده Sadr Scales 5.2.1 است. هدف این است که یک نرمافزار مجاز بتواند بدون دستکاری منطق پروتکل ترازو، عملیات مدیریتی و تبادل داده را از طریق SQL Server انجام دهد.
این راهنما دو سطح را پوشش میدهد: قرارداد عمومی SQL v1 برای یکپارچهسازی روزمره، و بخشهای پیشرفته و کنترلشده برای شناخت کامل Schema و عملیات مدیریتی.
• تعریف و ویرایش گروه کالا و شعبه.
• درج، تنظیم، غیرفعالسازی و گروهبندی ترازو.
• درج و ویرایش کالا، کلید سریع و نگاشت اختصاصی کالا به ترازو.
• خواندن فروش به دو روش تخت (SADR_Logs) و ساختیافته (SADR_Total + SADR_Detail)
• علامتگذاری فروش پردازششده، جستوجو با بارکد تجمیعی فاکتور و رعایت روابط و محدودیتهای Schema.
در مسیر عمومی v1، نرمافزار ثالث معمولاً SADR_ItemClass و SADR_Item را مینویسد و SADR_Logs را فقط میخواند. نوشتن در Registry/Mapping یا جداول فروش ساختیافته فقط در سناریوهای کنترلشده این سند انجام شود. سقف مجاز ترازو و قواعد تجاری نصب نیز باید رعایت شوند.
۱.۱ معماری یکپارچهسازی
نرمافزار ثالث برای عملیات روزمره نباید پروتکل اختصاصی مدلهای ترازو را دوباره پیادهسازی کند. رابط عمومی این نسخه، قرارداد SQL Server است و Sadr Scales مسئول Registry، Session، Retry، ارسال/دریافت و تفاوت مدلهای ترازو باقی میماند.
POS / ERP / Accounting
↓
Sadr Scales SQL Contract v1
↓
Sadr Scales Runtime
↓
PLUS / LSG / Aclas / مدلهای پشتیبانیشده
در استفاده معمول، نرمافزار ثالث یکی یا چند مورد زیر را انجام میدهد:
• ثبت یا ویرایش گروه و کالا از طریق Queryهای Parameterized.
• مدیریت شعبه و Registry/Mapping فقط در صورت نیاز واقعی و بهصورت کنترلشده.
• خواندن فروش در مسیر پایه از SADR_Logs؛ استفاده از SADR_Total + SADR_Detail برای سناریوی ساختیافته و کنترلشده.
• نگهداری Cursor یا وضعیت مصرف فروش در سیستم مقصد به شکل پایدار و Idempotent.
این سند فقط قابلیتهای موجود و قابل استفاده در Sadr Scales 5.2.1 را مستند میکند. در این نسخه رابط عمومی یکپارچهسازی SQL Server است و REST API/Webhook عمومی ارائه نشده است.
۲. قواعد پایه قبل از هر اتصال
۱. تمام Queryها باید Parameterized باشند.
۲. عملیات چندجدولی باید داخل Transaction انجام شوند.
۳. هیچ نرمافزار ثالثی نباید rowversion/timestamp را دستی مقداردهی کند.
۴. برای غیرفعالسازی ترازو از SADR_Scale.Used = 0 استفاده کنید؛ حذف فیزیکی ترازو برای غیرفعالسازی ممنوع است.
۵. ستونهای Runtime مانند Status، LastSendItem، LastSendKey و LastReceiveFID را بدون دلیل قراردادی تغییر ندهید.
۶. تغییر مستقیم SQL، متدهای Runtime برنامه را فراخوانی نمیکند؛ بنابراین برای تغییرات Registry/Connection بهتر است Sadr Scales متوقف باشد یا پس از تغییر، Registry/اتصالها Reload یا برنامه Restart شود.
۷. StoreCode='0' و ItemClassCode='0' رکوردهای پیشفرض Schema هستند و نباید بیدلیل حذف شوند.
۸. حذف رکورد Master میتواند به دلیل Foreign Keyهای ON DELETE CASCADE داده وابسته را حذف کند؛ قبل از هر DELETE روابط این سند را بررسی کنید.
۹. Batchهای بزرگ را به اندازههای قابل کنترل تقسیم کنید و Transaction طولانی در زمان اوج فروش نگه ندارید.
۱۰. در Deadlock یا خطاهای موقت SQL، Retry محدود با فاصله افزایشی انجام دهید؛ Retry نامحدود مجاز نیست.
۱۱. Connection String، نام کاربری و رمز عبور مشتری نباید داخل Source عمومی، Log یا Repository ثبت شود.
۱۲. عملیات Import و مصرف فروش باید Idempotent طراحی شوند تا اجرای دوباره باعث ثبت مالی تکراری نشود.
قرارداد عمومی SQL v1 برای کار روزمره بر
SADR_ItemClass، SADR_Item و خواندن SADR_Logs تکیه دارد. بخشهای
Registry/Mapping و وضعیتهای فروش ساختیافته در این راهنما برای سناریوهای
کنترلشده و شناخت کامل Schema آمدهاند. فیلدهای Runtime و جدول Sync داخلی
همچنان در مالکیت خود Sadr Scales هستند.
۳. فهرست کامل Objectهای اصلی پایگاه داده
| دسترسی پیشنهادی نرمافزار ثالث | نقش | نوع | Object |
|---|---|---|---|
| Read / Insert / Update — قرارداد عمومی v1 | گروه کالا | Table | dbo.SADR_ItemClass |
| Read / Insert / Update کنترلشده؛ خارج از مسیر پایه v1 | شعبه/فروشگاه | Table | dbo.SADR_Store |
| Read / Insert / Update کنترلشده؛ Delete فقط با آگاهی از پاکشدن فروش | Registry و تنظیمات ترازو | Table | dbo.SADR_Scale |
| Read / Replace داخل Transaction؛ کنترلشده | رابطه چندبهچند ترازو و گروه کالا | Table | dbo.SADR_ScaleItemClass |
| Read / Insert / Update — قرارداد عمومی v1؛ DeleteFlag ترجیح دارد | Master کالا/PLU | Table | dbo.SADR_Item |
| Read / Insert / Update / Delete کنترلشده | کلید سریع گروهی | Table | dbo.SADR_KeyAssignment |
| Read / Replace داخل Transaction؛ کنترلشده | کد کالا و کلید سریع اختصاصی هر ترازو | Table | dbo.SADR_ScaleItemMap |
| Read؛ LableStatus فقط برای وضعیت مصرف همان نرمافزار مقصد | Header فاکتور | Table | dbo.SADR_Total |
| Read؛ ItemStatus در صورت نیاز همان مصرفکننده | ریز اقلام فاکتور | Table | dbo.SADR_Detail |
| Read-only — قرارداد عمومی v1 | Feed تخت فروش | Table | dbo.SADR_Logs |
| عمدتاً Read-only | تاریخچه تغییر قیمت در UI | Table | dbo.SADR_PriceLog |
| Internal؛ دستکاری ممنوع | وضعیت داخلی Semantic Sync | Table | dbo.SADR_ItemSyncState |
| Read؛ برای سازگاری و تشخیص نسخه تغییر | نمای کالا + نسخه Semantic/Raw | View | dbo.SADR_ViewPlu |
| Read؛ خود برنامه از آن برای CRUD نیز استفاده میکند | نمای کلید سریع + LastUpdate | View | dbo.SADR_ViewKey |
| Read-only؛ Legacy | نام سازگار قدیمی برای SADR_Logs | View | dbo.JHMA_Logs |
۴. نقشه روابط جداول
روابط مهم Schema:
• SADR_Item.ItemClassCode → SADR_ItemClass.ItemClassCode
• SADR_Scale.ItemClassCode → SADR_ItemClass.ItemClassCode
• SADR_Scale.StoreCode → SADR_Store.StoreCode
• SADR_ScaleItemClass.ScaleID → SADR_Scale.ScaleID با ON DELETE CASCADE
• SADR_ScaleItemClass.ItemClassCode → SADR_ItemClass.ItemClassCode
• SADR_ScaleItemMap.ScaleID → SADR_Scale.ScaleID با ON DELETE CASCADE
• SADR_ScaleItemMap.PluNo → SADR_Item.PluNo با ON DELETE CASCADE
• SADR_Total.ScaleID → SADR_Scale.ScaleID با ON DELETE CASCADE
• SADR_Detail.TotalID → SADR_Total.TotalID با ON DELETE CASCADE
• SADR_Logs.DeviceNo → SADR_Scale.ScaleID با ON DELETE CASCADE
• SADR_KeyAssignment.ItemClassCode → SADR_ItemClass.ItemClassCode
| نکته: `SADR_KeyAssignment.PluNo` در Schema فعلی Foreign Key مستقیم به `SADR_Item` ندارد، اما از نظر قرارداد باید به یک PLU معتبر اشاره کند. قبل از تعریف کلید سریع وجود کالا را بررسی کنید. |
|---|
۵. ترتیب امن ساخت دادههای Master
برای ایجاد یک محیط کامل، ترتیب پیشنهادی این است:
۱. ایجاد یا اطمینان از وجود SADR_Store.
۲. ایجاد یا اطمینان از وجود SADR_ItemClass.
۳. ایجاد SADR_Scale با گروه پیشفرض معتبر.
۴. ثبت گروههای اضافی ترازو در SADR_ScaleItemClass.
۵. درج کالاها در SADR_Item با ItemClassCode معتبر.
۶. تعریف SADR_KeyAssignment برای کلیدهای گروهی در صورت نیاز.
۷. تعریف SADR_ScaleItemMap برای مدلهایی که نگاشت اختصاصی هر ترازو دارند.
۸. در تغییر گروه/Map، Cursorهای ارسال مرتبط را طبق الگوی برنامه Reset کنید.
۶. جدول گروه کالا — SADR_ItemClass
ساختار:
| توضیح | الزام | نوع | ستون |
|---|---|---|---|
| کد یکتای گروه | PK / NOT NULL | varchar(50) | ItemClassCode |
| نام گروه | Nullable | nvarchar(100) | ItemClassName |
| توضیح | Nullable | nvarchar(150) | Descriptions |
Schema بهصورت پیشفرض گروه 0 با نام «پیشفرض» میسازد.
الگوی Upsert مطابق رفتار Sadr Scales:
IF NOT EXISTS
(
SELECT 1
FROM dbo.SADR_ItemClass
WHERE ItemClassCode = @ItemClassCode
)
BEGIN
INSERT INTO dbo.SADR_ItemClass
(ItemClassCode, ItemClassName, Descriptions)
VALUES
(@ItemClassCode, @ItemClassName, @Descriptions);
END
ELSE
BEGIN
UPDATE dbo.SADR_ItemClass
SET ItemClassName = @ItemClassName,
Descriptions = @Descriptions
WHERE ItemClassCode = @ItemClassCode;
END;قبل از حذف گروه بررسی کنید که در SADR_Item، SADR_Scale، SADR_ScaleItemClass یا SADR_KeyAssignment استفاده نشده باشد. برای جابهجایی گروه، ابتدا وابستگیها را به گروه دیگری منتقل کنید.
۷. جدول شعبه — SADR_Store
ساختار:
| توضیح | الزام | نوع | ستون |
|---|---|---|---|
| کد یکتای شعبه | PK / NOT NULL | varchar(50) | StoreCode |
| نام شعبه | Nullable | nvarchar(100) | StoreName |
| توضیح | Nullable | nvarchar(150) | Descriptions |
Schema رکورد پیشفرض StoreCode='0' را ایجاد میکند.
IF NOT EXISTS
(
SELECT 1 FROM dbo.SADR_Store WHERE StoreCode = @StoreCode
)
INSERT INTO dbo.SADR_Store(StoreCode, StoreName, Descriptions)
VALUES(@StoreCode, @StoreName, @Descriptions);
ELSE
UPDATE dbo.SADR_Store
SET StoreName=@StoreName, Descriptions=@Descriptions
WHERE StoreCode=@StoreCode;SADR_Scale.StoreCode به این جدول Foreign Key دارد. SADR_Scale.StoreName نیز یک متن نمایشی مستقل است؛ در درج معمول فعلی Sadr Scales، StoreName ثبت میشود و StoreCode میتواند روی مقدار پیشفرض 0 باقی بماند. اگر Integration شما Master شعبه را مدیریت میکند، هر دو را سازگار نگهدارید.
۸. Registry ترازو — SADR_Scale
۸.۱ ستونها
| توضیح | مالکیت | نوع | ستون |
|---|---|---|---|
| شماره یکتای ترازو؛ صفر ممنوع | Config | int | ScaleID |
| پورت ارتباط | Config | int | Port |
| MAC/Serial در مدلهای مربوط | Runtime/Hardware | varchar(50) | Mac |
| FK به شعبه؛ پیشفرض 0 | Config | varchar(50) | StoreCode |
| گروه اصلی/پیشفرض؛ FK | Config | varchar(50) | ItemClassCode |
| مدل: LSG, LSG_24D, TSG, PLUS, LS6 | Config | nvarchar(50) | Category |
| نسخه دستگاه | Runtime/Hardware | varchar(50) | Version |
| نام نمایشی ترازو | Config | nvarchar(50) | DeviceName |
| نام نمایشی شعبه/محل | Config | nvarchar(50) | StoreName |
| IP ترازو | Config | varchar(20) | ScaleIP |
| Online/Offline؛ برای فعال/غیرفعالکردن استفاده نشود | Runtime | varchar(50) | Status |
| Cursor/نسخه آخرین ارسال کالا | Runtime | bigint | LastSendItem |
| Cursor/نسخه آخرین ارسال کلید | Runtime | bigint | LastSendKey |
| Cursor دریافت فروش در مدل مربوط | Runtime | int | LastReceiveFID |
| 0، 40 یا 63 | Config | smallint | HotKeyCountPerPage |
| 0 یا 1 تا 3 | Config | tinyint | HotKeyPageCount |
| ارسال خودکار تغییر کالا | Config | bit | AutoSendItems |
| دریافت خودکار فروش | Config | bit | AutoGetInvoice |
| درخواست/وضعیت ارسال تنظیمات به دستگاه | Runtime/Handshake | bit | SendScaleDetail |
| درخواست/وضعیت دریافت تنظیمات | Runtime/Handshake | bit | GetScaleDetail |
| 1 فعال، 0 غیرفعال | Config | int | Used |
قیدهای مهم:
ScaleID <> 0
(ScaleIP, Port) در DB یکتا است.
خود Sadr Scales هنگام درج، تکراری بودن کل ScaleIP یا ScaleID را رد میکند؛ Integration نیز همین قانون سختگیرانهتر را رعایت کند.
HotKey layout فقط 0/0 یا 40 یا 63 کلید در صفحه و 1 تا 3 صفحه است.
۸.۲ پیشفرض مدلها
| چیدمان کلید سریع پیشفرض | AutoGetInvoice در UI | پورت پیشفرض | مدل |
|---|---|---|---|
| 63 × 3 | قابل انتخاب؛ پیشفرض خاموش | 33581 | LSG |
| 63 × 3 | قابل انتخاب؛ پیشفرض خاموش | 33581 | LSG_24D |
| 40 × 3 | قابل انتخاب؛ پیشفرض خاموش | 33581 | TSG |
| 0 × 0 | روشن و در UI اجباری | 5000 | PLUS |
| 0 × 0 در Registry | قابل استفاده | 5002 | LS6 |
۸.۳ درج ترازو
درج باید Transactional باشد و همزمان رکورد گروه اولیه را در SADR_ScaleItemClass بسازد.
SET XACT_ABORT ON;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
IF EXISTS
(
SELECT 1
FROM dbo.SADR_Scale WITH (UPDLOCK, HOLDLOCK)
WHERE ScaleIP = @ScaleIP OR ScaleID = @ScaleID
)
THROW 50001, 'Duplicate ScaleIP or ScaleID', 1;
INSERT INTO dbo.SADR_Scale
(
ScaleID, ScaleIP, Port, Category,
DeviceName, StoreName, StoreCode, ItemClassCode,
AutoSendItems, AutoGetInvoice,
GetScaleDetail, SendScaleDetail, Used,
HotKeyCountPerPage, HotKeyPageCount
)
VALUES
(
@ScaleID, @ScaleIP, @Port, @Category,
@DeviceName, @StoreName, @StoreCode, @ItemClassCode,
@AutoSendItems, @AutoGetInvoice,
1, 0, @Used,
@HotKeyCountPerPage, @HotKeyPageCount
);
INSERT INTO dbo.SADR_ScaleItemClass(ScaleID, ItemClassCode)
VALUES(@ScaleID, @ItemClassCode);
UPDATE dbo.SADR_Scale
SET LastSendItem = 0, LastSendKey = 0
WHERE ScaleID = @ScaleID;
COMMIT;قبل از درج مطمئن شوید StoreCode و ItemClassCode موجودند. همچنین فعالسازی یا افزودن ترازو نباید سقف لایسنس نصب را دور بزند.
۸.۴ ویرایش ترازو
در UI فعلی 5.2.1 عملیات ویرایش روزمره روی نام، نام شعبه، گروه اصلی، AutoSend، AutoGet و Used انجام میشود. تغییر مستقیم IP/Port/Model از طریق SQL ممکن است، اما چون Runtime Session بر پایه این فیلدهاست باید فقط در حالت کنترلشده انجام شود و پس از آن Registry/Connection Reload یا Restart گردد.
نمونه ویرایش تنظیمات اصلی:
SET XACT_ABORT ON;
BEGIN TRANSACTION;
UPDATE dbo.SADR_Scale
SET DeviceName = @DeviceName,
StoreName = @StoreName,
StoreCode = @StoreCode,
ItemClassCode = @ItemClassCode,
AutoSendItems = @AutoSendItems,
AutoGetInvoice = @AutoGetInvoice,
Used = @Used,
GetScaleDetail = 0,
SendScaleDetail = 1,
LastSendItem = 0,
LastSendKey = 0
WHERE ScaleID = @ScaleID;
DELETE FROM dbo.SADR_ScaleItemClass
WHERE ScaleID = @ScaleID;
INSERT INTO dbo.SADR_ScaleItemClass(ScaleID, ItemClassCode)
VALUES(@ScaleID, @ItemClassCode);
COMMIT;اگر ترازو چند گروه دارد، بعد از Update گروه اصلی، همه گروههای موردنظر را طبق بخش SADR_ScaleItemClass دوباره درج کنید.
۸.۵ غیرفعالکردن ترازو
روش درست:
UPDATE dbo.SADR_Scale
SET Used = 0
WHERE ScaleID = @ScaleID;فعالسازی مجدد:
UPDATE dbo.SADR_Scale
SET Used = 1
WHERE ScaleID = @ScaleID;DELETE FROM SADR_Scale حذف نکنید. حذف فیزیکی ترازو میتواند اطلاعات فروش
ثبتشده در SADR_Logs، اطلاعات فاکتور در جداول Header/Detail و همچنین
Mappingهای وابسته را حذف کند. دلیل این رفتار، وجود روابط Foreign Key با
Cascade Delete در ساختار فعلی پایگاه داده است.
قرار دادن ستون Status روی مقدار Offline معادل غیرفعالکردن ترازو نیست؛ این ستون توسط Runtime برای وضعیت اتصال استفاده میشود.
۹. گروههای اختصاصیافته به ترازو — SADR_ScaleItemClass
این جدول رابطه چندبهچند ترازو و گروه کالا را نگه میدارد.
| نقش | ستون |
|---|---|
| FK به ترازو | ScaleID |
| FK به گروه کالا | ItemClassCode |
Primary Key: (ScaleID, ItemClassCode).
برای جایگزینی گروههای یک ترازو، همان الگوی خود برنامه را استفاده کنید:
SET XACT_ABORT ON;
BEGIN TRANSACTION;
DELETE FROM dbo.SADR_ScaleItemClass
WHERE ScaleID = @ScaleID;
INSERT INTO dbo.SADR_ScaleItemClass(ScaleID, ItemClassCode)
VALUES(@ScaleID, @Group1),
(@ScaleID, @Group2);
UPDATE dbo.SADR_Scale
SET LastSendItem = 0
WHERE ScaleID = @ScaleID;
COMMIT;حداقل یک گروه معتبر داشته باشید و بهتر است SADR_Scale.ItemClassCode نیز در این مجموعه وجود داشته باشد.
۱۰. Master کالا — SADR_Item
۱۰.۱ ستونها
| توضیح | نوع عملی | ستون | # |
|---|---|---|---|
| Legacy/کمکی | int | ID | 1 |
| Legacy/کمکی | int | IDitem | 2 |
| گروه کالا؛ FK | varchar(50) | ItemClassCode | 3 |
| شناسه یکتا و غیرصفر | int | PluNo | 4 |
| واحد کالا؛ پیشفرض 3 | int | PluUnit | 5 |
| قیمت واحد | int | UnitPrice | 6 |
| فرمت چاپ | int | PrintFormat | 7 |
| قیمت/هزینه کمکی | int | PluCost | 8 |
| فرمت بارکد | int | BarFormat | 9 |
| پرچمهای بارکد | int | BarFlags | 10 |
| کد کالا | varchar(10) | ItemCode | 11 |
| بارکد/ایندکس | varchar(50) | IndexBarcode | 12 |
| وزن ظرف | float | Tare | 13 |
| تاریخ/عمر مصرف قراردادی | int | ShelfDate | 14 |
| چاپ Shelf Date | int | ShelfDatePrint | 15 |
| چاپ تاریخ فروش | int | SaleDatePrint | 16 |
| چاپ ساعت فروش | int | SaleTimePrint | 17 |
| Mode/Tare در SQL | int | OnlyTare | 18 |
| شماره/کد مالیات | int | TaxNo | 19 |
| نرخ مالیات | float | TaxRate | 20 |
| نام کالا | nvarchar(100) | PluName | 21 |
| متنهای تکمیلی | nvarchar(100) | Text1..Text7 | 22-28 |
| حذف منطقی/غیرفعالسازی کالا | int | DeleteFlag | 29 |
| فیلد سازگار قدیمی/ارسال | int | SendFlag | 30 |
| داخلی SQL؛ هرگز دستی ننویسید | rowversion | TimeStamp | 31 |
PluNo یکتا است و مقدار صفر ممنوع است. ستونهای TaxNo و SendFlag در Schema وجود دارند، اما نمونههای قرارداد عمومی v1 آنها را نمینویسند و مقدار پیشفرض پایگاه داده حفظ میشود.
| نکته: `OnlyTare` نام فیلد SQL است. در Excel Import خود Sadr Scales، ستون متناظر با مفهوم `PluMode` خوانده میشود؛ این دو نام را در Integration با هم اشتباه نکنید. |
|---|
۱۰.۲ Upsert کالا
SET XACT_ABORT ON;
BEGIN TRANSACTION;
IF NOT EXISTS (SELECT 1 FROM dbo.SADR_Item WHERE PluNo=@PluNo)
BEGIN
INSERT INTO dbo.SADR_Item
(
ItemClassCode, PluNo, PluUnit, UnitPrice,
PrintFormat, PluCost, BarFormat, BarFlags,
ItemCode, IndexBarcode, Tare,
ShelfDate, ShelfDatePrint, SaleDatePrint, SaleTimePrint,
OnlyTare, TaxRate, PluName,
Text1, Text2, Text3, Text4, Text5, Text6, Text7,
DeleteFlag
)
VALUES
(
@ItemClassCode, @PluNo, @PluUnit, @UnitPrice,
@PrintFormat, @PluCost, @BarFormat, @BarFlags,
@ItemCode, @IndexBarcode, @Tare,
@ShelfDate, @ShelfDatePrint, @SaleDatePrint, @SaleTimePrint,
@OnlyTare, @TaxRate, @PluName,
@Text1, @Text2, @Text3, @Text4, @Text5, @Text6, @Text7,
@DeleteFlag
);
END
ELSE
BEGIN
UPDATE dbo.SADR_Item
SET ItemClassCode=@ItemClassCode,
PluUnit=@PluUnit,
UnitPrice=@UnitPrice,
PrintFormat=@PrintFormat,
PluCost=@PluCost,
BarFormat=@BarFormat,
BarFlags=@BarFlags,
ItemCode=@ItemCode,
IndexBarcode=@IndexBarcode,
Tare=@Tare,
ShelfDate=@ShelfDate,
ShelfDatePrint=@ShelfDatePrint,
SaleDatePrint=@SaleDatePrint,
SaleTimePrint=@SaleTimePrint,
OnlyTare=@OnlyTare,
TaxRate=@TaxRate,
PluName=@PluName,
Text1=@Text1, Text2=@Text2, Text3=@Text3, Text4=@Text4,
Text5=@Text5, Text6=@Text6, Text7=@Text7,
DeleteFlag=@DeleteFlag
WHERE PluNo=@PluNo;
END;
COMMIT;بهترین عملکرد این است که فقط در صورت تغییر واقعی محتوا UPDATE کنید. Sadr Scales 5.2.1 Semantic Fingerprint دارد و UPDATE بیاثر را از تغییر واقعی جدا میکند، اما نوشتن بیدلیل همه رکوردها در هر Cycle توصیه نمیشود.
۱۰.۳ حذف کالا
برای Integration عمومی، DeleteFlag را به حذف فیزیکی ترجیح دهید تا چرخه همگامسازی بتواند حذف/غیرفعالشدن کالا را مشاهده کند.
حذف فیزیکی SADR_Item میتواند رکوردهای SADR_ScaleItemMap همان PLU را به دلیل Cascade Delete حذف کند. جدول Sync داخلی نیز باید توسط خود Sadr Scales مدیریت شود.
۱۱. کلید سریع گروهی — SADR_KeyAssignment
ساختار:
| توضیح | ستون |
|---|---|
| گروه کالا | ItemClassCode |
| شماره صفحه؛ در قرارداد معمول صفرمبنا | PageNo |
| شماره کلید داخل صفحه | KeyNo |
| PLU اختصاصیافته | PluNo |
| rowversion داخلی | TimeStamp |
Primary Key: (ItemClassCode, PageNo, KeyNo).
Upsert:
IF EXISTS
(
SELECT 1
FROM dbo.SADR_KeyAssignment
WHERE ItemClassCode=@Group
AND PageNo=@PageNo
AND KeyNo=@KeyNo
)
UPDATE dbo.SADR_KeyAssignment
SET PluNo=@PluNo
WHERE ItemClassCode=@Group
AND PageNo=@PageNo
AND KeyNo=@KeyNo;
ELSE
INSERT INTO dbo.SADR_KeyAssignment
(ItemClassCode, PageNo, KeyNo, PluNo)
VALUES
(@Group, @PageNo, @KeyNo, @PluNo);برای آزادکردن یک کلید میتوانید رکورد را حذف کنید. پس از تغییر کلید سریع گروهی، برای ترازوهای مربوط LastSendKey=0 قرار دهید تا ارسال بعدی از ابتدا ارزیابی شود.
برای LSG/TSG چیدمان UI به این شکل است:
LSG/LSG_24D: 63کلید در هر صفحه، 3 صفحه پیشفرض.
TSG: 40کلید در هر صفحه، 3 صفحه پیشفرض.
PageNoصفرمبنا است؛ مثلاً در چیدمان 63تایی، کلید نمایشی 64 برابر PageNo=1, KeyNo=1 است.
در LS6/Aclas قرارداد کلیدهای خاص و رزروشده وجود دارد؛ نرمافزار ثالث نباید بدون تطبیق با منطق مدل، کلیدهای ویژه را بازنویسی کند.
۱۲. نگاشت اختصاصی کالا به هر ترازو — SADR_ScaleItemMap
این جدول با SADR_KeyAssignment فرق دارد. KeyAssignment گروهمحور است؛ ScaleItemMap برای یک ترازوی مشخص کد محلی کالا و در صورت نیاز جای کلید سریع را نگه میدارد.
در UI 5.2.1 این قابلیت برای مدلهای LSG, LSG_24D, TSG ارائه میشود.
| محدودیت | ستون |
|---|---|
| ترازو؛ بخشی از PK | ScaleID |
| PLU؛ بخشی از PK و FK به کالا | PluNo |
| کد محلی روی ترازو؛ > 0 و در هر Scale یکتا | ItemCode |
| Null یا 0..2 | PageNo |
| Null یا > 0 | KeyNo |
قواعد:
PK = (ScaleID, PluNo)
(ScaleID, ItemCode) یکتا است.
(ScaleID, PageNo, KeyNo) در صورت داشتن HotKey یکتا است.
PageNoو KeyNo باید یا هر دو Null باشند یا هر دو مقدار داشته باشند.
جای کلید باید داخل HotKeyCountPerPage و HotKeyPageCount همان ترازو باشد.
روش امن جایگزینی Map:
SET XACT_ABORT ON;
BEGIN TRANSACTION;
DELETE FROM dbo.SADR_ScaleItemMap
WHERE ScaleID=@ScaleID;
INSERT INTO dbo.SADR_ScaleItemMap
(ScaleID, PluNo, ItemCode, PageNo, KeyNo)
VALUES
(@ScaleID, @PluNo1, @ItemCode1, @PageNo1, @KeyNo1),
(@ScaleID, @PluNo2, @ItemCode2, NULL, NULL);
UPDATE dbo.SADR_Scale
SET LastSendItem=0,
LastSendKey=0
WHERE ScaleID=@ScaleID;
COMMIT;قبل از Commit همه Duplicateها و محدوده HotKey را Validate کنید. خود Sadr Scales همین عملیات را Transactional انجام میدهد.
۱۳. Semantic Syncداخلی — SADR_ItemSyncState و SADR_ViewPlu
SADR_ItemSyncState جدول داخلی برنامه است:
| توضیح | ستون |
|---|---|
| PK | PluNo |
| SHA-like fingerprint محتوای موثر | ContentFingerprint |
| آخرین rowversion مشاهدهشده | LastObservedRowVersion |
| آخرین تغییر واقعی محتوایی | LastSemanticRowVersion |
نرمافزار ثالث نباید در این جدول Insert/Update/Delete کند.
SADR_ViewPlu نمای کالا است و علاوه بر ستونهای SADR_Item، دو مقدار زیر را ارائه میدهد:
LastUpdate: نسخه Semantic برای تغییر واقعی محتوا.
RawLastUpdate: مقدار خام rowversion.
برای نوشتن مستقیم Integration، SADR_Item شفافتر و پایدارتر است؛ View را بیشتر برای Read/Compatibility استفاده کنید.
۱۴. معماری ذخیره فروش در 5.2.1
هر فاکتور پذیرفتهشده در مسیر Durable به شکل Transactional در سه نمایش ذخیره میشود:
۱. SADR_Total: یک Header برای کل فاکتور.
۲. SADR_Detail: یک یا چند ردیف برای اقلام فاکتور.
۳. SADR_Logs: Feed تخت؛ یک ردیف برای هر قلم فروش.
Sadr Scales برای Duplicate detection، وجود کامل Header/Detail/Logs را بررسی میکند. بنابراین نرمافزار ثالث نباید رکوردهای فروش را حذف یا ناقص کند.
۱۵. روش اول خواندن فروش — SADR_Logs
SADR_Logs برای نرمافزارهایی مناسب است که یک Feed ساده خطبهخط میخواهند.
۱۵.۱ ستونها
| معنا | ستون |
|---|---|
| Identity و Cursor مناسب برای مصرفکننده | ID |
| شماره ترازو؛ FK به SADR_Scale.ScaleID | DeviceNo |
| شناسه/IP ثبتشده هنگام فروش | Identify |
| زمان فروش | DateTime |
| شناسه فاکتور سمت ترازو/پروتکل | FID |
| شناسه کمکی فروش | SID |
| فروشنده/اپراتور | Salesman |
| شماره ردیف فاکتور | SubID |
| مبلغ ردیف | TotalPrice |
| PLU کالا | PLU |
| کلاس/گروه دریافتی | Class |
| Department دریافتی | Dept |
| مقدار/وزن | Amount |
| واحد | Unit |
| نوع لاگ/فروش | LogType |
| مالیات | Tax |
| متنهای دریافتی | Text1..Text4 |
| قیمت واحد | UnitPrice |
| شناسه همکار/کمکی پروتکل | CoFID |
| نام کالا | PLUName |
Unique Key: (DeviceNo, FID, SubID).
خواندن افزایشی:
SELECT TOP (@BatchSize)
ID, DeviceNo, Identify, [DateTime], FID, SID,
Salesman, SubID, TotalPrice, PLU, Class, Dept,
Amount, Unit, LogType, Tax,
Text1, Text2, Text3, Text4,
UnitPrice, CoFID, PLUName
FROM dbo.SADR_Logs
WHERE ID > @LastProcessedId
ORDER BY ID ASC;در روش SADR_Logs، بهتر است نرمافزار مصرفکننده Cursor LastProcessedId را در دیتابیس خودش نگه دارد و رکورد Sadr Scales را Update/Delete نکند.
۱۶. روش دوم خواندن فروش — SADR_Total + SADR_Detail
این روش برای POS/ERPهایی مناسب است که Header و ریز اقلام فاکتور را عیناً میخواهند.
۱۶.۱ Header — SADR_Total
| معنا | ستون |
|---|---|
| Identity داخلی و Unique | TotalID |
| شماره ترازو | ScaleID |
| تاریخ/زمان فروش | SaleDateTime |
| وضعیت پردازش برای مصرفکننده؛ پیشفرض 0 | LableStatus |
| شماره رسید/فاکتور | ReceiptNo |
| بارکد تجمیعی کل فاکتور | TotalBarcode |
| بارکد یکتای Header؛ Primary Key | ItemBarcode |
| تعداد اقلام/تراکنشهای فاکتور | NTrans |
| مبلغ تخفیف فرعی | SubDiscAmt |
| تخفیف | DiscAmt |
| مالیات A | AmtOfATax |
| مالیات V | AmtOfVTax |
| مبلغ کل ثبتشده فاکتور | PriceWTax |
| فروشنده/کاربر | ClerkNo |
۱۶.۲ Detail — SADR_Detail
| معنا | ستون |
|---|---|
| Identity و PK | DetailID |
| FK به Header | TotalID |
| همان بارکد تجمیعی Header | TotalBarcode |
| بارکد اختصاصی ردیف | ItemBarcode |
| شماره ردیف/SubID | TransNo |
| PLU کالا | PluNo |
| مقدار برای کالاهای وزنی (Unit=3 در مسیر درج فعلی) | Weight |
| تعداد برای کالاهای غیر وزنی | QTY |
| قیمت واحد | Uprice |
| قیمت واحد بعد از تخفیف - فیلد سازگار | UpriceAfDisc |
| وضعیت/مقدار تخفیف سازگار | StPointDiscStat |
| مبلغ تخفیف کل ردیف | TTLPriceDiscAmt |
| مبلغ واقعی ردیف | ActPrice |
| مقدار مالیات ذخیرهشده در مسیر فعلی | TaxRtNo |
| وضعیت پردازش خطی؛ در صورت نیاز مصرفکننده | ItemStatus |
رابطه اصلی:
| SADR_Total.TotalID = SADR_Detail.TotalID |
|---|
۱۷. بارکد تجمیعی فاکتور
در مسیر فعلی 5.2.1، Sadr Scales بارکد تجمیعی را به شکل زیر میسازد:
| TotalBarcode = "25" + ScaleID(D3) + FID(D9) |
|---|
همین مقدار عیناً در دو محل ذخیره میشود:
SADR_Total.TotalBarcode
SADR_Detail.TotalBarcode برای تمام ردیفهای همان فاکتور
بنابراین اگر POS بارکد تجمیعی چاپشده در انتهای فاکتور را اسکن کند، میتواند مستقیم Header و Detail را بازیابی کند.
SELECT
t.TotalID, t.ScaleID, t.SaleDateTime,
t.ReceiptNo, t.TotalBarcode, t.ItemBarcode,
t.NTrans, t.PriceWTax, t.ClerkNo, t.LableStatus,
d.DetailID, d.TransNo, d.PluNo,
d.Weight, d.QTY, d.Uprice, d.ActPrice,
d.ItemBarcode AS DetailItemBarcode,
d.ItemStatus
FROM dbo.SADR_Total t
LEFT JOIN dbo.SADR_Detail d
ON d.TotalID = t.TotalID
WHERE t.TotalBarcode = @ScannedBarcode
ORDER BY d.DetailID;برای فیلتر دقیقتر در محیط چند ترازو میتوانید ScaleID را نیز اضافه کنید.
SADR_Total.ItemBarcode با TotalBarcode فرق دارد. برای جستوجوی بارکد تجمیعی انتهای فاکتور از `TotalBarcode` استفاده کنید.
۱۸. جلوگیری از خواندن دوباره فروش — Status/Acknowledgement
Sadr Scales هنگام درج Header مقدار LableStatus را بهصورت پیشفرض 0 میگذارد و Detail با ItemStatus=0 درج میشود.
در معماری این سند، هر نصب Sadr Scales یک نرمافزار فروشگاهی/حسابداری مقصد دارد. برای روش ساختیافته میتوان قرارداد زیر را برای وضعیت پردازش همان مقصد استفاده کرد:
LableStatus = 0: فاکتور هنوز توسط نرمافزار مصرفکننده پردازش نشده است.
LableStatus = 1: فاکتور با موفقیت مصرف/ثبت شده است.
ItemStatus = 0/1: در صورت نیاز همین مفهوم در سطح ردیف.
خواندن فروش و Ack را در دو مرحله کوتاه انجام دهید؛ تراکنش پایگاه داده Sadr Scales را هنگام ثبت فاکتور در سیستم مقصد باز نگه ندارید.
-- Step 1: read the next unprocessed invoice.
DECLARE @TotalID int;
SELECT TOP (1)
@TotalID = TotalID
FROM dbo.SADR_Total
WHERE ISNULL(LableStatus, 0) = 0
ORDER BY TotalID;
IF @TotalID IS NOT NULL
BEGIN
SELECT *
FROM dbo.SADR_Total
WHERE TotalID = @TotalID;
SELECT *
FROM dbo.SADR_Detail
WHERE TotalID = @TotalID
ORDER BY DetailID;
END;-- Step 2: run only after the destination commit succeeds.
SET XACT_ABORT ON;
BEGIN TRANSACTION;
UPDATE dbo.SADR_Total
SET LableStatus = 1
WHERE TotalID = @TotalID
AND ISNULL(LableStatus, 0) = 0;
UPDATE dbo.SADR_Detail
SET ItemStatus = 1
WHERE TotalID = @TotalID
AND ISNULL(ItemStatus, 0) = 0;
COMMIT;برای اسکن بارکد نیز همین ترتیب را رعایت کنید: ابتدا فاکتور را در POS ثبت و Commit کنید، سپس TotalID همان فاکتور را Ack کنید. LableStatus/ItemStatus وضعیت همان نرمافزار مقصد این نصب هستند و برای Cursor داخلی دریافت از ترازو استفاده نمیشوند.
۱۹. دریافت فاکتور کامل بر اساس ترازو و FID
SADR_Logs با (DeviceNo, FID, SubID) یکتا است. در Structured Sales، TotalBarcode از ScaleID+FID ساخته میشود. بنابراین برای یک ترازو و FID میتوان سه نمایش را تطبیق داد.
DECLARE @TotalBarcode varchar(50) =
'25' + RIGHT('000' + CAST(@ScaleID AS varchar(10)), 3)
+ RIGHT('000000000' + CAST(@FID AS varchar(20)), 9);
SELECT *
FROM dbo.SADR_Total
WHERE ScaleID=@ScaleID AND TotalBarcode=@TotalBarcode;
SELECT d.*
FROM dbo.SADR_Detail d
INNER JOIN dbo.SADR_Total t ON t.TotalID=d.TotalID
WHERE t.ScaleID=@ScaleID AND t.TotalBarcode=@TotalBarcode
ORDER BY d.DetailID;
SELECT *
FROM dbo.SADR_Logs
WHERE DeviceNo=@ScaleID AND FID=@FID
ORDER BY SubID;این سه Query برای عیبیابی Integration و کنترل کاملبودن فاکتور بسیار مفیدند.
۲۰. JHMA_Logs — سازگاری نسخههای قدیمی
dbo.JHMA_Logs در Schema فعلی یک View روی dbo.SADR_Logs است، نه جدول اصلی فروش.
برای Integration جدید از SADR_Logs استفاده کنید. JHMA_Logs فقط برای سازگاری نرمافزارهای قدیمی که نام قبلی را انتظار دارند نگه داشته شده است.
۲۱. تاریخچه قیمت — SADR_PriceLog
ساختار:
| توضیح | ستون |
|---|---|
| Identity | ID |
| PLU | PluNo |
| بارکد/ایندکس | IndexBarcode |
| نام کالا | PluName |
| قیمت قبلی | LastPrice |
| قیمت جدید | NewPrice |
| زمان تغییر | DateTime |
| کاربر؛ پیشفرض Admin | User |
این جدول در UI بهعنوان Price Log خوانده میشود. برای Integration عمومی، تغییر قیمت اصلی را روی SADR_Item.UnitPrice انجام دهید؛ SADR_PriceLog را جدول Master قیمت تلقی نکنید.
۲۲. Viewهای LastUpdate
SADR_ViewPlu
نمای کالا با:
LastUpdate: تغییر Semantic.
RawLastUpdate: rowversion خام.
SADR_ViewKey
نمای SADR_KeyAssignment با:
LastUpdate = CONVERT(bigint, TimeStamp)
این Viewها برای تشخیص تغییر و سازگاری داخلی مهماند. نرمافزار ثالث نباید مقدار LastUpdate را بنویسد.
۲۳. Cursorها و فیلدهای Runtime که نباید آزادانه تغییر کنند
| قاعده | مالک | فیلد |
|---|---|---|
| فقط وضعیت اتصال؛ Enabled نیست | Runtime | SADR_Scale.Status |
| فقط هنگام تغییر Group/Map طبق الگوی برنامه Reset به 0 | Runtime/Sync | LastSendItem |
| فقط هنگام تغییر Key/Map طبق الگوی برنامه Reset به 0 | Runtime/Sync | LastSendKey |
| Cursor دریافت؛ دستکاری آزاد ممنوع | Runtime | LastReceiveFID |
| Flag هماهنگی تنظیمات | Runtime | SendScaleDetail |
| Flag هماهنگی تنظیمات | Runtime | GetScaleDetail |
| معمولاً از دستگاه تکمیل میشوند | Hardware/Runtime | Mac, Version |
| کاملاً داخلی | Runtime | SADR_ItemSyncState.* |
| هرگز دستی ننویسید | SQL Server | TimeStamp/rowversion |
۲۴. عملیات حذف و خطر Cascade
حذف ترازو
حذف فیزیکی Scale در مسیر خود برنامه عمداً موارد زیر را پاک میکند:
SADR_Logs مربوط به ترازو.
SADR_Detail مربوط به فاکتورهای ترازو.
SADR_Total مربوط به ترازو.
SADR_ScaleItemMap
SADR_ScaleItemClass
خود SADR_Scale.
بنابراین حذف Scale یک عملیات پاکسازی داده است، نه غیرفعالسازی.
حذف کالا
به دلیل FK SADR_ScaleItemMap.PluNo -> SADR_Item.PluNo ON DELETE CASCADE، حذف فیزیکی کالا نگاشتهای اختصاصی آن را نیز حذف میکند.
حذف Header فاکتور
حذف SADR_Total، ردیفهای SADR_Detail همان TotalID را Cascade حذف میکند.
۲۵. سناریوی کامل: ساخت گروه، ترازو، کالا و کلید سریع
SET XACT_ABORT ON;
BEGIN TRANSACTION;
-- 1) Group
IF NOT EXISTS (SELECT 1 FROM dbo.SADR_ItemClass WHERE ItemClassCode='10')
INSERT INTO dbo.SADR_ItemClass(ItemClassCode, ItemClassName, Descriptions)
VALUES('10', N'Fruit', N'Fruit group');
-- 2) Store
IF NOT EXISTS (SELECT 1 FROM dbo.SADR_Store WHERE StoreCode='1')
INSERT INTO dbo.SADR_Store(StoreCode, StoreName, Descriptions)
VALUES('1', N'Main Store', N'');
-- 3) Scale
IF NOT EXISTS (SELECT 1 FROM dbo.SADR_Scale WHERE ScaleID=1 OR ScaleIP='192.168.1.50')
BEGIN
INSERT INTO dbo.SADR_Scale
(
ScaleID, ScaleIP, Port, Category,
StoreCode, StoreName, ItemClassCode, DeviceName,
HotKeyCountPerPage, HotKeyPageCount,
AutoSendItems, AutoGetInvoice, Used
)
VALUES
(
1, '192.168.1.50', 33581, 'LSG',
'1', N'Main Store', '10', N'Checkout Scale 1',
63, 3,
1, 0, 1
);
INSERT INTO dbo.SADR_ScaleItemClass(ScaleID, ItemClassCode)
VALUES(1, '10');
END;
-- 4) Item
IF NOT EXISTS (SELECT 1 FROM dbo.SADR_Item WHERE PluNo=1001)
INSERT INTO dbo.SADR_Item
(ItemClassCode, PluNo, PluName, PluUnit, UnitPrice, DeleteFlag)
VALUES
('10', 1001, N'Apple', 3, 150000, 0);
ELSE
UPDATE dbo.SADR_Item
SET ItemClassCode='10', PluName=N'Apple',
PluUnit=3, UnitPrice=150000, DeleteFlag=0
WHERE PluNo=1001;
-- 5) Group HotKey: page 0 key 1
IF EXISTS
(
SELECT 1 FROM dbo.SADR_KeyAssignment
WHERE ItemClassCode='10' AND PageNo=0 AND KeyNo=1
)
UPDATE dbo.SADR_KeyAssignment
SET PluNo=1001
WHERE ItemClassCode='10' AND PageNo=0 AND KeyNo=1;
ELSE
INSERT INTO dbo.SADR_KeyAssignment(ItemClassCode,PageNo,KeyNo,PluNo)
VALUES('10',0,1,1001);
UPDATE dbo.SADR_Scale
SET LastSendItem=0, LastSendKey=0
WHERE ScaleID=1;
COMMIT;بعد از این تغییرات، اگر Sadr Scales در حال اجراست Registry/Connection را Reload/Restart کنید تا تنظیمات ترازو قطعی اعمال شوند.
۲۶. سناریوی کامل: دریافت و Ack فروش اسکنشده
-- Step 1: read the scanned invoice.
DECLARE @TotalID int;
SELECT TOP (1)
@TotalID = TotalID
FROM dbo.SADR_Total
WHERE TotalBarcode = @ScannedBarcode;
IF @TotalID IS NULL
THROW 50001, 'Invoice barcode not found', 1;
SELECT *
FROM dbo.SADR_Total
WHERE TotalID = @TotalID;
SELECT *
FROM dbo.SADR_Detail
WHERE TotalID = @TotalID
ORDER BY DetailID;فاکتور را با شناسه یکتای خود در سیستم مقصد بهصورت Idempotent ثبت کنید. فقط پس از Commit موفق مقصد، وضعیت پردازش را در یک تراکنش کوتاه در پایگاه داده Sadr Scales بهروزرسانی کنید:
SET XACT_ABORT ON;
BEGIN TRANSACTION;
UPDATE dbo.SADR_Total
SET LableStatus = 1
WHERE TotalID = @TotalID
AND ISNULL(LableStatus, 0) = 0;
UPDATE dbo.SADR_Detail
SET ItemStatus = 1
WHERE TotalID = @TotalID
AND ISNULL(ItemStatus, 0) = 0;
COMMIT;۲۷. کنترل صحت Schema قبل از شروع Integration
IF OBJECT_ID(N'dbo.SADR_ItemClass', N'U') IS NULL THROW 50001, 'SADR_ItemClass missing', 1;
IF OBJECT_ID(N'dbo.SADR_Store', N'U') IS NULL THROW 50002, 'SADR_Store missing', 1;
IF OBJECT_ID(N'dbo.SADR_Scale', N'U') IS NULL THROW 50003, 'SADR_Scale missing', 1;
IF OBJECT_ID(N'dbo.SADR_ScaleItemClass', N'U') IS NULL THROW 50004, 'SADR_ScaleItemClass missing', 1;
IF OBJECT_ID(N'dbo.SADR_Item', N'U') IS NULL THROW 50005, 'SADR_Item missing', 1;
IF OBJECT_ID(N'dbo.SADR_KeyAssignment', N'U') IS NULL THROW 50006, 'SADR_KeyAssignment missing', 1;
IF OBJECT_ID(N'dbo.SADR_ScaleItemMap', N'U') IS NULL THROW 50007, 'SADR_ScaleItemMap missing', 1;
IF OBJECT_ID(N'dbo.SADR_Total', N'U') IS NULL THROW 50008, 'SADR_Total missing', 1;
IF OBJECT_ID(N'dbo.SADR_Detail', N'U') IS NULL THROW 50009, 'SADR_Detail missing', 1;
IF OBJECT_ID(N'dbo.SADR_Logs', N'U') IS NULL THROW 50010, 'SADR_Logs missing', 1;اگر Database قدیمی است، ابتدا اجازه دهید Sadr Scales 5.2.1 Schema migration خود را اجرا کند؛ Integration نباید خودش Constraintهای داخلی را حذف یا بازسازی کند.
۲۸. سطح دسترسی پیشنهادی SQL
برای نرمافزار ثالث از حساب sa استفاده نکنید و Credential واقعی را در Source، فایل نمونه یا Log قرار ندهید. حداقل دسترسی لازم را بدهید:
• SELECT روی Objectهای موردنیاز.
• INSERT/UPDATE روی Group/Store/Scale/Item/Key/Map فقط اگر نرمافزار واقعاً این قابلیتها را دارد.
• UPDATE محدود روی SADR_Total.LableStatus و در صورت نیاز SADR_Detail.ItemStatus.
• بدون DELETE روی Sales برای Integration عادی.
• بدون Write روی SADR_ItemSyncState.
در محیط حساس بهتر است عملیات Write از Stored Procedureهای کنترلشده انجام شود تا Permission مستقیم جدول محدود بماند.
۲۹. سازگاری و نسخهبندی قرارداد
این سند قرارداد پایگاه داده و یکپارچهسازی Sadr Scales 5.2.1 با SQL Contract v1 را شرح میدهد. نرمافزار Integration باید نسخه قرارداد مورد انتظار خود را ثبت کند و تغییر Schema یا Contract را بدون بررسی سازگاری وارد محیط مشتری نکند.
Integration باید هنگام Startup نسخه قرارداد مورد انتظار خود را کنترل کند و قبل از استفاده از ستون/جدول جدید، وجود آن را با OBJECT_ID و COL_LENGTH بررسی کند.
Schema داخلی را صرفاً بر اساس حدس نسخه تغییر ندهید.
۳۰. چکلیست برنامهنویس
[ ] Connection String از تنظیمات امن خوانده میشود و داخل Source ثبت نشده است.
[ ] گروه و شعبه قبل از Masterهای وابسته ساخته میشوند.
[ ] ScaleID و ScaleIP تکراری نیستند.
[ ] فعالسازی Scale سقف لایسنس را دور نمیزند.
[ ] غیرفعالسازی با Used=0 انجام میشود.
[ ] گروه اصلی Scale در SADR_ScaleItemClass نیز وجود دارد.
[ ] PluNo غیرصفر و یکتا است.
[ ] TimeStamp, LastUpdate, ItemSyncState دستی نوشته نمیشوند.
[ ] KeyAssignment فقط به PLU معتبر اشاره میکند.
[ ] ScaleItemMap Duplicate ItemCode/HotKey ندارد و داخل Layout ترازو است.
[ ] تغییر Group/Map/Key Cursor ارسال مرتبط را طبق الگوی برنامه Reset میکند.
[ ] فروش از SADR_Logs با Cursor مستقل یا از Total/Detail با Ack خوانده میشود.
[ ] TotalBarcode برای اسکن فاکتور استفاده میشود، نه ItemBarcode Header.
[ ] Ack فقط بعد از ثبت موفق فروش در سیستم مقصد به 1 تغییر میکند.
[ ] هیچ DELETE عادی روی Scale/Sales انجام نمیشود.
[ ] عملیات چندجدولی Transactional و Idempotent هستند.
ضمیمه A. نمونه مرجع C# برای .NET Framework 4.x
کدهای این ضمیمه نمونه مرجع برای پیادهسازی SQL Contract v1 هستند. همه Queryها Parameterized هستند و از ADO.NET استاندارد استفاده میکنند. Credential واقعی مشتری را داخل Source قرار ندهید.
| نکته: این نمونهها مستقیماً با SQL Server کار میکنند و هیچ منطق پروتکل ترازو را پیادهسازی نمیکنند. مدیریت ارتباط با ترازو بر عهده Sadr Scales است. |
|---|
A.1 اتصال و کنترل Schema
using System;
using System.Data;
using System.Data.SqlClient;
public static class SadrSchema
{
public static SqlConnection Open(string connectionString)
{
SqlConnection connection = new SqlConnection(connectionString);
connection.Open();
return connection;
}
public static void Validate(SqlConnection connection)
{
const string sql = @"
IF OBJECT_ID(N'dbo.SADR_ItemClass', N'U') IS NULL
THROW 50001, 'SADR_ItemClass missing', 1;
IF OBJECT_ID(N'dbo.SADR_Store', N'U') IS NULL
THROW 50002, 'SADR_Store missing', 1;
IF OBJECT_ID(N'dbo.SADR_Scale', N'U') IS NULL
THROW 50003, 'SADR_Scale missing', 1;
IF OBJECT_ID(N'dbo.SADR_ScaleItemClass', N'U') IS NULL
THROW 50004, 'SADR_ScaleItemClass missing', 1;
IF OBJECT_ID(N'dbo.SADR_Item', N'U') IS NULL
THROW 50005, 'SADR_Item missing', 1;
IF OBJECT_ID(N'dbo.SADR_KeyAssignment', N'U') IS NULL
THROW 50006, 'SADR_KeyAssignment missing', 1;
IF OBJECT_ID(N'dbo.SADR_ScaleItemMap', N'U') IS NULL
THROW 50007, 'SADR_ScaleItemMap missing', 1;
IF OBJECT_ID(N'dbo.SADR_Total', N'U') IS NULL
THROW 50008, 'SADR_Total missing', 1;
IF OBJECT_ID(N'dbo.SADR_Detail', N'U') IS NULL
THROW 50009, 'SADR_Detail missing', 1;
IF OBJECT_ID(N'dbo.SADR_Logs', N'U') IS NULL
THROW 50010, 'SADR_Logs missing', 1;";
using (SqlCommand command = new SqlCommand(sql, connection))
{
command.CommandTimeout = 30;
command.ExecuteNonQuery();
}
}
}A.2 مدل داده کالا
public sealed class SadrItem
{
public string ItemClassCode { get; set; }
public int PluNo { get; set; }
public int PluUnit { get; set; }
public int UnitPrice { get; set; }
public int PrintFormat { get; set; }
public int PluCost { get; set; }
public int BarFormat { get; set; }
public int BarFlags { get; set; }
public string ItemCode { get; set; }
public string IndexBarcode { get; set; }
public double Tare { get; set; }
public int ShelfDate { get; set; }
public int ShelfDatePrint { get; set; }
public int SaleDatePrint { get; set; }
public int SaleTimePrint { get; set; }
public int OnlyTare { get; set; }
public double TaxRate { get; set; }
public string PluName { get; set; }
public string Text1 { get; set; }
public string Text2 { get; set; }
public string Text3 { get; set; }
public string Text4 { get; set; }
public string Text5 { get; set; }
public string Text6 { get; set; }
public string Text7 { get; set; }
public int DeleteFlag { get; set; }
}A.3 ایجاد یا ویرایش گروه کالا
public static void UpsertItemClass(
SqlConnection connection,
string code,
string name,
string description)
{
const string sql = @"
IF NOT EXISTS
(
SELECT 1
FROM dbo.SADR_ItemClass
WHERE ItemClassCode = @Code
)
BEGIN
INSERT INTO dbo.SADR_ItemClass
(ItemClassCode, ItemClassName, Descriptions)
VALUES
(@Code, @Name, @Description);
END
ELSE
BEGIN
UPDATE dbo.SADR_ItemClass
SET ItemClassName = @Name,
Descriptions = @Description
WHERE ItemClassCode = @Code;
END;";
using (SqlCommand command = new SqlCommand(sql, connection))
{
command.Parameters.Add("@Code", SqlDbType.VarChar, 50).Value = code;
command.Parameters.Add("@Name", SqlDbType.NVarChar, 100).Value =
(object)name ?? DBNull.Value;
command.Parameters.Add("@Description", SqlDbType.NVarChar, 150).Value =
(object)description ?? DBNull.Value;
command.ExecuteNonQuery();
}
}A.4 Upsert کامل کالا داخل Transaction
public static void UpsertItem(SqlConnection connection, SadrItem item)
{
const string sql = @"
IF NOT EXISTS (SELECT 1 FROM dbo.SADR_Item WHERE PluNo=@PluNo)
BEGIN
INSERT INTO dbo.SADR_Item
(
ItemClassCode, PluNo, PluUnit, UnitPrice,
PrintFormat, PluCost, BarFormat, BarFlags,
ItemCode, IndexBarcode, Tare,
ShelfDate, ShelfDatePrint, SaleDatePrint, SaleTimePrint,
OnlyTare, TaxRate, PluName,
Text1, Text2, Text3, Text4, Text5, Text6, Text7,
DeleteFlag
)
VALUES
(
@ItemClassCode, @PluNo, @PluUnit, @UnitPrice,
@PrintFormat, @PluCost, @BarFormat, @BarFlags,
@ItemCode, @IndexBarcode, @Tare,
@ShelfDate, @ShelfDatePrint, @SaleDatePrint, @SaleTimePrint,
@OnlyTare, @TaxRate, @PluName,
@Text1, @Text2, @Text3, @Text4, @Text5, @Text6, @Text7,
@DeleteFlag
);
END
ELSE
BEGIN
UPDATE dbo.SADR_Item
SET ItemClassCode=@ItemClassCode,
PluUnit=@PluUnit,
UnitPrice=@UnitPrice,
PrintFormat=@PrintFormat,
PluCost=@PluCost,
BarFormat=@BarFormat,
BarFlags=@BarFlags,
ItemCode=@ItemCode,
IndexBarcode=@IndexBarcode,
Tare=@Tare,
ShelfDate=@ShelfDate,
ShelfDatePrint=@ShelfDatePrint,
SaleDatePrint=@SaleDatePrint,
SaleTimePrint=@SaleTimePrint,
OnlyTare=@OnlyTare,
TaxRate=@TaxRate,
PluName=@PluName,
Text1=@Text1, Text2=@Text2, Text3=@Text3, Text4=@Text4,
Text5=@Text5, Text6=@Text6, Text7=@Text7,
DeleteFlag=@DeleteFlag
WHERE PluNo=@PluNo;
END;";
using (SqlTransaction tx = connection.BeginTransaction())
using (SqlCommand command = new SqlCommand(sql, connection, tx))
{
command.Parameters.Add("@ItemClassCode", SqlDbType.VarChar, 50).Value = item.ItemClassCode;
command.Parameters.Add("@PluNo", SqlDbType.Int).Value = item.PluNo;
command.Parameters.Add("@PluUnit", SqlDbType.Int).Value = item.PluUnit;
command.Parameters.Add("@UnitPrice", SqlDbType.Int).Value = item.UnitPrice;
command.Parameters.Add("@PrintFormat", SqlDbType.Int).Value = item.PrintFormat;
command.Parameters.Add("@PluCost", SqlDbType.Int).Value = item.PluCost;
command.Parameters.Add("@BarFormat", SqlDbType.Int).Value = item.BarFormat;
command.Parameters.Add("@BarFlags", SqlDbType.Int).Value = item.BarFlags;
command.Parameters.Add("@ItemCode", SqlDbType.VarChar, 10).Value =
(object)item.ItemCode ?? DBNull.Value;
command.Parameters.Add("@IndexBarcode", SqlDbType.VarChar, 50).Value =
(object)item.IndexBarcode ?? DBNull.Value;
command.Parameters.Add("@Tare", SqlDbType.Float).Value = item.Tare;
command.Parameters.Add("@ShelfDate", SqlDbType.Int).Value = item.ShelfDate;
command.Parameters.Add("@ShelfDatePrint", SqlDbType.Int).Value = item.ShelfDatePrint;
command.Parameters.Add("@SaleDatePrint", SqlDbType.Int).Value = item.SaleDatePrint;
command.Parameters.Add("@SaleTimePrint", SqlDbType.Int).Value = item.SaleTimePrint;
command.Parameters.Add("@OnlyTare", SqlDbType.Int).Value = item.OnlyTare;
command.Parameters.Add("@TaxRate", SqlDbType.Float).Value = item.TaxRate;
command.Parameters.Add("@PluName", SqlDbType.NVarChar, 100).Value =
(object)item.PluName ?? DBNull.Value;
command.Parameters.Add("@DeleteFlag", SqlDbType.Int).Value = item.DeleteFlag;
string[] values = { item.Text1, item.Text2, item.Text3, item.Text4,
item.Text5, item.Text6, item.Text7 };
for (int i = 0; i < values.Length; i++)
{
command.Parameters.Add("@Text" + (i + 1), SqlDbType.NVarChar, 100).Value =
(object)values[i] ?? DBNull.Value;
}
command.ExecuteNonQuery();
tx.Commit();
}
}برای عملکرد بهتر، UPDATE را فقط در صورت
تغییر واقعی محتوا انجام دهید. rowversion خام ممکن است با UPDATE دوباره
تغییر کند، اما Semantic Change توسط Sadr Scales بر اساس محتوای مؤثر
تشخیص داده میشود.
A.5 خواندن افزایشی فروش از SADR_Logs
public static DataTable ReadSalesAfterId(
SqlConnection connection,
long lastProcessedId,
int batchSize)
{
const string sql = @"
SELECT TOP (@BatchSize)
ID, DeviceNo, Identify, [DateTime], FID, SID,
Salesman, SubID, TotalPrice, PLU, Class, Dept,
Amount, Unit, LogType, Tax,
Text1, Text2, Text3, Text4,
UnitPrice, CoFID, PLUName
FROM dbo.SADR_Logs
WHERE ID > @LastProcessedId
ORDER BY ID ASC;";
using (SqlCommand command = new SqlCommand(sql, connection))
{
command.Parameters.Add("@BatchSize", SqlDbType.Int).Value = batchSize;
command.Parameters.Add("@LastProcessedId", SqlDbType.BigInt).Value = lastProcessedId;
using (SqlDataAdapter adapter = new SqlDataAdapter(command))
{
DataTable table = new DataTable();
adapter.Fill(table);
return table;
}
}
}پس از ثبت موفق هر Batch در سیستم مقصد، بزرگترین ID ثبتشده را در پایگاه داده خود نرمافزار مقصد نگهدارید. رکوردهای SADR_Logs را برای مدیریت Cursor تغییر ندهید.
A.6 دریافت فاکتور کامل با TotalBarcode
public static DataSet ReadInvoiceByBarcode(
SqlConnection connection,
string totalBarcode)
{
const string headerSql = @"
SELECT *
FROM dbo.SADR_Total
WHERE TotalBarcode=@TotalBarcode;";
const string detailSql = @"
SELECT d.*
FROM dbo.SADR_Detail d
INNER JOIN dbo.SADR_Total t ON t.TotalID=d.TotalID
WHERE t.TotalBarcode=@TotalBarcode
ORDER BY d.DetailID;";
DataSet result = new DataSet();
using (SqlCommand header = new SqlCommand(headerSql, connection))
{
header.Parameters.Add("@TotalBarcode", SqlDbType.VarChar, 50).Value = totalBarcode;
using (SqlDataAdapter adapter = new SqlDataAdapter(header))
adapter.Fill(result, "Header");
}
using (SqlCommand detail = new SqlCommand(detailSql, connection))
{
detail.Parameters.Add("@TotalBarcode", SqlDbType.VarChar, 50).Value = totalBarcode;
using (SqlDataAdapter adapter = new SqlDataAdapter(detail))
adapter.Fill(result, "Detail");
}
return result;
}A.7 Ack فاکتور
public static void AcknowledgeInvoice(
SqlConnection connection,
int totalId)
{
const string sql = @"
UPDATE dbo.SADR_Total
SET LableStatus=1
WHERE TotalID=@TotalID
AND ISNULL(LableStatus,0)=0;
UPDATE dbo.SADR_Detail
SET ItemStatus=1
WHERE TotalID=@TotalID
AND ISNULL(ItemStatus,0)=0;";
using (SqlTransaction tx = connection.BeginTransaction())
using (SqlCommand command = new SqlCommand(sql, connection, tx))
{
command.Parameters.Add("@TotalID", SqlDbType.Int).Value = totalId;
command.ExecuteNonQuery();
tx.Commit();
}
}Ack را فقط بعد از ثبت موفق و Commit
فاکتور در سیستم مقصد انجام دهید. در صورت قطع ارتباط بین ثبت مقصد و Ack،
ثبت مقصد باید Idempotent باشد تا پردازش دوباره باعث فروش تکراری
نشود.
A.8 نمونه جریان ساده برنامه
static void Main()
{
string connectionString = LoadConnectionStringFromSecureSettings();
using (SqlConnection connection = SadrSchema.Open(connectionString))
{
SadrSchema.Validate(connection);
UpsertItemClass(connection, "10", "Fruit", "Fruit group");
DataTable sales = ReadSalesAfterId(connection, 0, 100);
foreach (DataRow row in sales.Rows)
{
// 1) Map row to the destination model.
// 2) Save it idempotently in the destination database.
// 3) Persist the last successfully processed SADR_Logs.ID.
}
}
}ضمیمه B. پیادهسازی همین قرارداد در زبانهای دیگر
SQL Contract به زبان برنامهنویسی وابسته نیست. اگر نرمافزار مقصد با C#/.NET نوشته نشده است، همان قواعد این سند باید در Driver یا کتابخانه SQL زبان مقصد رعایت شوند.
| قاعده | رفتار لازم |
|---|---|
| Parameterized Query | هیچ مقدار ورودی با الحاق رشته داخل SQL ساخته نشود. |
| Transaction | عملیات چندجدولی مانند Scale + ScaleItemClass یا Ack Header + Detail اتمیک باشد؛ تراکنش پایگاه داده هنگام ثبت در سیستم خارجی باز نگه داشته نشود. |
| Idempotency | اجرای دوباره Import یا دریافت فروش باعث رکورد مالی تکراری نشود. |
| Cursor فروش | LastProcessedId یا State مصرفکننده در سیستم مقصد نگهداری شود. |
| Runtime Fields | Status، LastReceiveFID، rowversion و SADR_ItemSyncState آزادانه نوشته نشوند. |
| Scale Disable | برای غیرفعالسازی از Used=0 استفاده شود؛ DELETE معادل Disable نیست. |
| Security | Credential در Source/Log ذخیره نشود و حساب SQL حداقل Permission لازم را داشته باشد. |
| Compatibility | قبل از استفاده از Object یا ستون، وجود Schema مورد انتظار کنترل شود. |
نمونههای C# ضمیمه A را میتوان بهعنوان مرجع رفتاری دید: نام جدولها، ترتیب Transaction، Parameterها، Cursor فروش و Ack باید در زبان مقصد همان معنا را حفظ کنند.
چک سریع قبل از تحویل Integration
• [ ] Schema مورد انتظار روی دیتابیس مشتری کنترل شده است.
• [ ] هیچ Credential واقعی داخل Source یا Log وجود ندارد.
• [ ] همه Queryها Parameterized هستند.
• [ ] نوشتن چندجدولی Transactional است.
• [ ] Import کالا در اجرای دوباره نتیجه تکراری یا ناسازگار ایجاد نمیکند.
• [ ] Cursor فروش فقط بعد از ثبت موفق مقصد جلو میرود.
• [ ] Ack فاکتور فقط بعد از Commit مقصد انجام میشود.
• [ ] DELETE روی Scale/Sales در عملیات عادی وجود ندارد.
• [ ] فیلدهای Runtime/Internal خارج از قرارداد تغییر داده نمیشوند.