Amirali Yaghoutiمهندس ارشد نرم‌افزار

مطالعه موردی مهندسی

جعبه اصلاح سفارش A2: ابزار تعویض قلم و اصلاح قیمت

ساعتی آسیب‌دیده می‌رسد و با رفرنس دیگری تعویض می‌شود. مشتری بخشی از مبلغ را نقد سر پیشخوان می‌دهد. قیمتی اشتباه وارد شده. WooCommerce بعد از پرداخت برای هیچ‌کدام مسیر امنی ندارد؛ برای همین A2 Order Fix Box را ساختم: متاباکسی روی صفحه‌ی ویرایش سفارش گالری جواهریان که فروشنده در آن قلم را عوض می‌کند، ردیف حمل اضافه می‌کند یا قیمت را اصلاح می‌کند، بی‌آنکه دستش به درگاه پرداخت یا وضعیت سفارش بخورد.

مسئله تجاری

در فروشگاه ساعت و جواهر، سفارش بعد از پرداخت بیشتر از آنچه فکر می‌کنید تغییر می‌کند: مشتری مدل دیگری برمی‌دارد، جمع یک ردیف اشتباه از آب درمی‌آید، یا یک رفت‌وبرگشت اضافی پیک لازم می‌شود. ویرایش سفارش پرداخت‌شده در WooCommerce شدنی است و خطرناک؛ پنل ادمین اجازه می‌دهد قلم‌ها را عوض کنید، اما هیچ‌جا ثبت نمی‌شود سفارش قبلش چه شکلی بود، undo وجود ندارد، و هر تغییری که به جمع پرداخت دست بزند ممکن است با آنچه درگاه واقعاً گرفته جور درنیاید. ویرایش دستی جمع‌هایی می‌ساخت که کسی نمی‌توانست ردشان را بگیرد و کوچک‌ترین لغزش رکورد پرداخت را خراب می‌کرد. تیم باید این سه اصلاح را به‌طور روزمره انجام می‌داد و من ابزاری می‌خواستم که سفارش را تعمیر کند و در عین حال درگاه و وضعیت سفارش را کاملاً به حال خودشان بگذارد.

آنچه تحویل دادم

  • ماژول a2-order-replacement-box.php، یک متاباکس ۳٬۲۹۴ خطی روی صفحه‌ی ویرایش سفارش با سه عملیات: تعویض یک قلم با یک یا چند محصول، افزودن ردیف حمل اضافه، و اصلاح جمع ردیف همان قلم.
  • اسنپ‌شات کامل سفارش در _a2_fix_snapshot_v1 که پیش از هر تغییر گرفته می‌شود، با undo تک‌کلیکی روی wp_ajax_a2_fix_undo که همان را برمی‌گرداند؛ اصلاح اشتباه به‌جای اینکه به یک تعمیر دستی دوم ختم شود، برگشت می‌خورد.
  • رکورد پرداخت تفکیک‌شده که پول آمده از درگاه را از کارت‌به‌کارت و دیگر دریافتی‌های خارج از درگاه جدا می‌کند و به‌شکل ردیف‌های تفکیکی در _a2_fix_offline_rows_json می‌نشیند؛ باقی‌مانده هم بین سهم درگاه و سهم کارت‌به‌کارت تفکیک می‌شود.
  • لاگ تاریخچه در _a2_fix_history با یک خلاصه‌ی فارسی خوانا برای هر اصلاح، به‌علاوه‌ی یک یادداشت سفارش در هر اکشن، تا ترتیب اصلاح‌ها ماه‌ها بعد هم خوانا بماند.
  • نشان، ستون و نمای فیلتر برای سفارش‌های اصلاح‌شده، هم روی فهرست قدیمی shop_order و هم روی جدول wc-orders در HPOS، تا سفارش‌های تعمیرشده دسته‌جمعی پیدا شوند، نه یکی‌یکی.
  • متای ثبت بازپرداخت داخلی، تا پولی که بیرون از درگاه برمی‌گردد روی خود سفارش بنشیند، نه در حافظه‌ی یک نفر.
  • یک قانون سفت که در خود نام افزونه هم آمده: وضعیت سفارش و درگاه پرداخت هرگز دست نمی‌خورند.

رویکرد فنی

  • «اول اسنپ‌شات، بعد تغییر» ثابتی است که کل ابزار روی آن بنا شده؛ تا وضعیت قبلی ذخیره نشود، هیچ‌چیز روی سفارش عوض نمی‌شود.
  • هر اکشن از admin-ajax می‌گذرد، پشت یک nonce و یک بررسی سطح دسترسی (manage_woocommerce یا edit_shop_orders).
  • یک مرحله‌ی محاسبه (wp_ajax_a2_fix_calc) جمع قدیم، جمع جدید و اختلاف علامت‌دار را پیش از اعمال نشان می‌دهد.
  • دست‌نزدن به درگاه یک «نه» عمدی است. ابزار فقط بدهی فروشگاه و مبلغ دریافتی را اصلاح می‌کند و هیچ‌وقت آن را در رکورد پرداخت منعکس نمی‌کند، چون مرجع آنچه واقعاً دریافت شده خود درگاه است. وضعیت سفارش را هم تغییر نمی‌دهد؛ فقط اقلام، کارمزدها و کلیدهای متای خودش را با پیشوند _a2_fix_ می‌نویسد.
  • دریافتی‌های خارج از درگاه دسته‌ی خودشان را دارند و در جمع سفارش حل نمی‌شوند، تا حسابداری بعداً هم قابل تفکیک بماند.
  • ورودی‌های عددی ارقام فارسی و عربی را نرمال می‌کنند و جداکننده‌ی هزارگان را پیش از پارس حذف می‌کنند، چون کارکنان مبلغ را از فاکتور کپی می‌کنند.
  • ردپای ممیزی خودبه‌خود از استفاده‌ی ابزار می‌آید، نه از مرحله‌ای که کسی باید یادش بماند: هر اعمال، هم یک سطر به تاریخچه اضافه می‌کند و هم یک یادداشت روی سفارش می‌نویسد.
  • مخزن نمایشی عمومی همین الگو را تعمیم می‌دهد: کنترلرهای فقط-ادمین با بررسی دسترسی، منطق عملیات در سرویس‌ها نه در هندلرهای UI، فلگ‌های عملیاتی جدا از داده‌ی پرداخت، بارگذاری اسکریپت فقط روی صفحه‌ی مربوط، و یک بررسی سینتکس PHP روی نمونه‌ها. این یک مخزن نمایشی است، نه بسته‌ی پروداکشن.

نتیجه و شواهد

ماژول در ادمین فروشگاه زنده کار می‌کند. زمان صرفه‌جویی‌شده را رسماً اندازه نگرفته‌ام. آنچه تأیید شده این‌هاست: هر نوشتن پشت nonce و بررسی دسترسی است، پیش از هر تغییر اسنپ‌شات وجود دارد و undo همان را برمی‌گرداند، دریافتی‌های خارج از درگاه در ردیف‌های خودشان می‌نشینند، و سفارش‌های اصلاح‌شده روی هر دو جدول سفارش دیده می‌شوند. شاهد عملی این است که آن سه اصلاح روزمره واقعاً روزمره شدند: کارکنان سفارش‌های استثنایی را داخل همین یک باکس و با مسیر undo تعمیر می‌کنند، نه با ویرایش خام سفارش، و تاریخچه‌ی سفارش حالا خودش را توضیح می‌دهد؛ همین بود که مغایرت‌گیری حسابداری را شدنی کرد.

اهمیت برای کارفرما

اینجا نقطه‌ی برخورد عملیات و حسابداری است. تعمیر سفارش قبلاً کاری بود که فقط من می‌توانستم بی‌خطر انجامش بدهم؛ حالا خود فروشنده‌ها انجامش می‌دهند. در خرده‌فروشی ساعت و جواهر، اصلاح سفارش هر جور شده اتفاق می‌افتد؛ سؤال فقط این بود که ردی از خودش جا می‌گذارد یا نه. حالا می‌گذارد، و تصویر حسابداری هر سفارش اصلاح‌شده برای هرکسی که بعداً سراغش برود روشن می‌ماند.

خلاصه-اجرایی-پروژه

خلاصه اجرایی خوانا

خلاصه_اجرایی {
  پروژه: "A2 Order Fix Box"
  پشته: "افزونه‌ی MU وردپرس، WooCommerce، admin-ajax"
  ماژول: "a2-order-replacement-box.php (۳٬۲۹۴ خط)"
  عملیات: "تعویض قلم (۱..n محصول)، ردیف حمل اضافه،
           اصلاح قیمت روی همان قلم"
  ajax: "a2_fix_calc, a2_fix_apply, a2_fix_rebuild_totals, a2_fix_undo"
  متا: "_a2_fix_snapshot_v1, _a2_fix_history, _a2_fix_offline_rows_json"
  ثابت: "اسنپ‌شات پیش از تغییر؛ undo بازش می‌گرداند"
  هرگز: "بدون فراخوان درگاه، بدون تغییر وضعیت"
  حسابداری: "دریافتی خارج از درگاه جدا ثبت می‌شود؛
             باقی‌مانده تفکیک می‌شود: سهم درگاه و سهم کارت‌به‌کارت"
  ممیزی: "تاریخچه‌ی متا + یادداشت سفارش در هر اکشن، با nonce و بررسی دسترسی"
  یافتن: "نشان، ستون و فیلتر روی shop_order و wc-orders در HPOS"
  مخزن: "github.com/shiny-a2/a2-woocommerce-admin-ops-toolkit"
  وضعیت: "در پروداکشن روی ادمین فروشگاه"
}

ارزش حرفه‌ای این پروژه

این مهندسی پشت‌صحنه کنار پول واقعی است و جالب‌ترین بخشش رابط کاربری نیست. مهم‌ترین خط این پروژه همان است که می‌گوید درگاه را دست نزن: خیلی راحت می‌شد با دست‌کاری رکورد پرداخت اعداد را مرتب نشان داد، و دقیقاً همان کار است که مغایرت‌گیری بعدی را غیرممکن می‌کند. پس ابزار با کارهایی تعریف می‌شود که حق ندارد بکند: نه فراخوان درگاه، نه تغییر وضعیت، نه ویرایش بی‌سروصدا.

سطح نوشتن را به چند کلید متای پیشونددار محدود کردم و هر تغییر را برگشت‌پذیر ساختم. اسنپ‌شات و undo درخواست کسی نبودند؛ شرط خودم بودند برای اینکه حاضر شوم ابزاری دست تیم بدهم که سفارش پرداخت‌شده را ویرایش می‌کند. هر چیزی که کنار داده‌ی پرداخت قرار بگیرد را همین‌طور می‌سازم.