مطالعه موردی مهندسی
میز تعمیرات و خدمات پس از فروش
ساعتی که یک هفته روی میز میماند، یک رابطهی مشتری است که پول تویش هست. میز تعمیرات همانجایی از فروشگاه بود که سابقهاش یک کشو بود.
مسئله تجاری
تعمیرات و خدمات پس از فروش این فروشگاه روی کاغذ و حافظه میچرخید: تکنسین عیب را روی یک برگه مینوشت، قیمت پای تلفن توافق میشد، ساعت در هر کشویی که خالی بود میماند، و مشتری برمیگشت پیش هرکس که شیفت بود. هیچچیز پرونده را به حساب مشتری وصل نمیکرد؛ کسی که سه ساعت آنلاین خریده بود و یکی را برای سرویس گذاشته بود، برای سیستم دو غریبه بود.
مالک ایده را «خام» میدانست و از فاز اول خواست محورهایی را ببندد که وگرنه وسط ساخت دوباره بحث میشدند: کارمند چه واحدی وارد میکند و پایگاه چه واحدی ذخیره میکند، چه کسی پرونده را میبیند و عوض میکند، مشتری کجا قیمت را تأیید میکند، و آیا درآمد تعمیرات به باشگاه مشتریان میرسد یا نه. آن چهار تصمیم همهی چیزهای پایین را شکل داد.
آنچه تحویل دادم
- یک PWA تکنسین برای پروندههای تعمیر: پذیرش با فیلدهای اجباری عیب، وضعیت کارکرد و کشوی نگهداری، قیمتدهی، تأیید مشتری، پرداختها، و تحویل با رمز — با چیدمان سهستونی روی نمایشگر بزرگ و چیدمان گوشی روی میز.
- پذیرشی که از همان مسیر ساخت مشتری فروشگاه، برای شماره تلفن حساب باز میکند تا مشتری با رمز یکبارمصرف وارد شود، فاکتور تمیز را ببیند و قیمت را از منوی تعمیرات در پنل خودش تأیید کند — نه با جواب دادن به پیامک.
- جریان بستن پرونده که در آن هر تلاش پرداخت از یک برگهی باز، یک کلید بیاثریدرتکرار دارد که وقت بازشدن برگه ساخته شده؛ تسویه وقتی چیزی بدهکار نیست یا از مانده بیشتر است رد میشود؛ و تحویل پروندهای که قبلاً تحویل شده یک تکرار است که هیچچیز را دوبار نمینویسد.
- دو قیمت که مستقل دنبال میشوند — جمع خطوط و مبلغی که به مشتری پیامک شده — با نوار هشدار وقتی فرق دارند و دکمهی یکضربهای برای قیمتدهی دوباره؛ و فهرست بستهی دو کشو بهجای متن آزاد.
- سه دفترچهی قیمت که به سه سؤال متفاوت جواب میدهند: قیمت پایهی اعلامشده بر اساس رده، برند و استثنا؛ تاریخچهی یادگرفته که وقتی کار به «آماده» میرسد نوشته میشود؛ و باتری متناسب با رفرنس. هرجا هر دو باشند، تاریخچه برنده است؛ هرجا هیچکدام نباشد، جواب «پس از بررسی اعلام میشود» است.
- داشبورد مدیریت و خروجی اکسل که فقط برای ادمین است، پروندههای موقت را از هر شمارشی کنار میگذارد و مبلغها را عددی ذخیره میکند تا جمعزدنِ خودِ مالک کار کند؛ و تعمیراتی که با نرخ خودش از راه همان سرویس پاداش به باشگاه امتیاز میدهد، هیچوقت با نوشتن مستقیم در دفتر کل.
رویکرد فنی
- کارمند تومان وارد میکند و میبیند، پایگاه ریال ذخیره میکند، چون جدول سفارشهای حضوری از قبل ریالی بود و دو واحد در یک اسکیما همان راهی است که ماندهها غلط میشوند. تبدیل فقط در مرز ورودی و نمایش زندگی میکند.
- هر تکنسین هر پرونده را با قیمتهایش میبیند و عوض میکند، چون وقتی تکنسین پذیرش نیست همکارش ساعت را تحویل میدهد؛ داشبورد و خروجی فقط برای ادمین میماند. دسترسی دنبال کسی میرود که واقعاً کار را انجام میدهد.
- جریان بستن پرونده بعد از دو پروندهی واقعی بازسازی شد که با ماندهی منفی بزرگ بسته شده بودند: شش تسویهی یکسان در چهل ثانیه — پرداخت، رمز غلط، رد، دوباره ضربه — روی وایفای فروشگاه که اپ درخواستهای قطعشده را تکرار میکند. حالا هر چیز پولشکل یک کلید بیاثری به ازای هر برگه دارد نه هر ضربه، و سرور تسویهای را که چیزی توجیهش نمیکند رد میکند.
- برگهی تحویل پیش از ضربه به همهی دروازهها جواب میدهد. برگهی قدیمی گزینهی «بدون رمز» را داخل یک پنل جمعشده پنهان کرده بود و پشتش یک دروازهی دوم اعلامنشده بود؛ یک تکنسین سه بار با دلیلی که برای سرور کوتاه بود شکست خورد. حالا برگه انتخابها را نشان میدهد — رمز را دارد یا نه، الان پرداخت میکند یا نه — و هرکدام جعبهی دلیل خودش را باز میکند که کاراکترهای باقیمانده را میشمارد؛ تا همهی دروازهها رد نشوند چیزی فرستاده نمیشود. رمز فقط وقتی خواسته میشود که واقعاً برای مشتری فرستاده شده باشد.
- تاریخچهی قیمت وقتی کار به «آماده» میرسد ثبت میشود، هیچوقت موقع تحویل، چون تحویل همان کار را دوبار میشمرد؛ خطوط رایگان، خطوط بدون کلید و تعداد نادیده گرفته میشوند تا دفترچه، دفترچهی قیمت بماند نه دفترچهی فاکتور.
نتیجه و شواهد
ماژول روی میز یک فروشگاه ساعت و جواهر کار میکند و چهار تصمیم مالک از زمان اسکوپ تغییری نکرده. چیزی که تأیید کردم ثابتها هستند نه یک عدد سرعت: کلید پرداخت یکبار به ازای هر برگهی باز ساخته میشود و کلید تکراری «تکرار» برمیگرداند؛ پروندهی تحویلشده اگر دوباره تحویل شود سطر دوم در تایملاین نمینویسد و رویداد دوم نمیزند؛ پروندههای موقت در هیچ شمارشی نیستند؛ مبلغهای خروجی بدون فرمول در اکسل جمع میشوند. بازسازی جریان بستن از دو تسویهی خراب واقعی درآمد و برگهی دروازهها از سه ضربهی ناموفق یک تکنسین — هر دو در تایملاین هستند و هیچکدام تکرار نشده.
اهمیت برای کارفرما
میز خدمات جایی است که فروشگاه مشتری را بعد از فروش یا نگه میدارد یا از دست میدهد. وقتی پروندهی تعمیر در همان حسابی زندگی میکند که خریدها، مشتری قیمت را از گوشیاش تأیید میکند نه با تماس، تحویل نمیتواند دوبار اتفاق بیفتد، و مالک ماه را از اکسلی میخواند که جمعش درست است — خدمات پس از فروش از یک مرکز هزینه که کسی نمیبیند، به دلیل دوم برگشتن تبدیل میشود، و حالا مثل خرید امتیاز باشگاه هم میدهد.
خلاصه اجرایی خوانا
خلاصه_اجرایی {
پروژه: "میز تعمیرات و خدمات پس از فروش"
وضعیت: در حال کار روی میز یک فروشگاه
پذیرش: عیب + وضعیت کارکرد + کشو (اجباری) -> پروندهی موقت
هویت: شماره تلفن حساب مشتری را باز میکند -> ورود با OTP -> تأیید قیمت در پنل
پول: کارمند تومان میبیند، ذخیره ریال، تبدیل فقط در مرز
پرداخت: یک کلید بیاثری به ازای هر برگه؛ تسویهی بیمورد یا بیش از مانده رد میشود
تحویل: رمز فقط اگر فرستاده شده؛ مازاد قیمت را هر تکنسین با دلیل جواب میدهد
قیمت: پایهی اعلامشده -> تاریخچه (ثبت در «آماده») -> باتری متناسب
قاعده: تاریخچه بر اعلام مقدم است؛ نبودن = «پس از بررسی اعلام میشود»
گزارش: داشبورد + خروجی عددی، فقط ادمین، بدون پروندههای موقت
باشگاه: تعمیرات از راه سرویس پاداش امتیاز میدهد، نه با نوشتن مستقیم
نمایش عمومی: github.com/shiny-a2/a2-crm-operations-system
}ارزش حرفهای این پروژه
بخشی که دوست دارم یک بازبین بخواند جریان بستن پرونده است: دو تسویهی خراب تبدیل شد به یک کلید بیاثری با دامنهی تعریفشده، یک قاعدهی رد در سرور، و برگهی تحویلی که نیمهجواب نمیشود فرستاد. شکل بیشتر باگهای عملیاتی که درست میکنم همین است — نه یک قابلیت غایب، بلکه دروازهای که جای اشتباهی زندگی میکرد.
چیز دیگر آن چهار تصمیم اسکوپ است. بستن واحد پول، مدل دسترسی، سطح تأیید و درز باشگاه پیش از نوشتن کد یعنی ماژول وسط ساخت دوباره بحث نشد، و وقتی مالک بعداً تعمیرات را به باشگاه وصل کرد، آن درز از قبل تنها در بود.