A) إلغاء ثم إعادة ترحيل (الموصى به بعد الاعتماد)
- تحقق وصلاحيات: المستخدم، الفترة المحاسبية (غير مُقفلة)، حالة الفاتورة = completed.
- جلب الصورة الحالية: رأس الفاتورة + البنود + دفعات البنود + حركات المخزون + القيود المالية + حركة الخزنة + COGS.
- ابدأ معاملة واحدة (DB transaction) وقم بـ:
- عكس المخزون:
- لكل حركة sale سابقة تكتب حركة معاكسة (مثلاً
sale_reversal
أوadjustment_in
) بنفس الكمية والتكلفة، - ترجِع كميات الدُفعات وأرصدة المستودع،
- تُرجِع أثر الـ moving basis (تزيد الـ moving_qty_basis و moving_value_basis بالقيم التي كانت قد نُقصت).
- لكل حركة sale سابقة تكتب حركة معاكسة (مثلاً
- عكس القيود المالية:
- لكل قيد سابق تكتب قيدًا عكسيًا (نفس الحساب والمبلغ ولكن معكوس مدين/دائن)،
- تُحدِّث أرصدة الحسابات وفق التسلسل.
- عكس الخزينة (لو كانت نقدًا):
- تكتب سندًا عكسيًا (صرف بدل قبض بنفس القيمة) لتعود الخزنة لوضعها قبل الفاتورة،
- تنتبه لتسلسل أرصدة الخزائن بعد هذا التاريخ (انظر “إعادة توازن الأرصدة” بالأسفل).
- تغيير حالة الفاتورة الأصلية إلى “revised” أو “voided” مع حفظ رقمها كسجل تاريخي (لا تحذفها).
- عكس المخزون:
- بناء النسخة الجديدة:
- احسب البنود الجديدة والضرائب والمجاميع،
- خصّص الدُفعات FEFO من المخزون الحالي،
- احسب COGS وفق سياسة التكلفة الفعّالة.
- أعد الترحيل للنسخة الجديدة:
- اكتب حركات مخزون sale الجديدة، وحدث أرصدة المستودع وmoving basis،
- اكتب القيود المالية (إيراد/ضريبة/طرف مقابل نقدًا أو آجلاً)، وحدث أرصدة الحسابات،
- اكتب حركة الخزينة (لو نقدًا)،
- خزّن البنود/الدفعات الجديدة في جداولها.
- سجّل نشاط يوضح أن الفاتورة روجِعت، واربط الجديدة بالقديمة (reference_to=old_invoice_id).
- اعمل Commit.
لماذا هذا الأسلوب؟
لأنه يحافظ على أثر محاسبي نظيف لا يعبث بالماضي: كل ما حدث سابقًا يُعكس بحركات صريحة، ثم يُنشَر الجديد كوضع نهائي. التاريخ لا يُعاد كتابته؛ نضيف فقط سجلّات.