طفرات GraphQL (كتابة)

إنشاء وتحديث وحذف عبر GraphQL

6 دقيقة قراءة
طباعة هذا القسم

ابتداءً من خطة Business، يدعم مسار GraphQL اثنتي عشرة عملية كتابة (mutations): الإنشاء والتحديث والحذف على أوامر العمل والأصول وقطع الغيار، إضافةً إلى adjustStock لحركات المخزون. تمر هذه العمليات عبر الخدمات نفسها التي يستخدمها التطبيق، لذا يعمل سجل التدقيق والإشعارات والتوليد التلقائي للمراجع (WO-XX وASS-XX وPART-XX) بالطريقة نفسها. أما الحادية عشرة، addMeterReading، فتسجّل قراءات العدّادات (ساعات التشغيل، عدّادات القطع) — انظر أدناه. أما الثانية عشرة، setPurchaseOrderErpReference، فتكتب على أمر الشراء رقم المستند في نظام ERP لديك (بمفتاح وصول FULL فقط) ولا تُطلق أي webhook، فلا يتلقى نظام ERP كتابته هو مرتدّةً إليه أبدًا.

الطفرات المتاحة

  • createWorkOrder(input) → WorkOrder
  • updateWorkOrder(id, input) → WorkOrder
  • deleteWorkOrder(id) → DeleteResult
  • createAsset(input) → Asset
  • updateAsset(id, input) → Asset
  • deleteAsset(id, force?) → DeleteResult
  • createPart(input) → Part
  • updatePart(id, input) → Part
  • deletePart(id, force?) → DeleteResult
  • adjustStock(id, quantity, reason?, vendorId?, documentNumber?) → Part
  • addMeterReading(meterId, value?, incrementValue?, date?, notes?, confirmRegression?) → MeterReading
  • setPurchaseOrderErpReference(id, erpReference?) → PurchaseOrder

شكل الإدخال

تحتوي كل طفرة على InputType بحقول آمنة فقط — إذ تُملأ الحقول companyId وsequenceNumber وreference وcreatedById من جانب الخادم. ولا تمرّر سوى title وdescription وstatus وpriority وassetId وlocationId وما إلى ذلك. تقبل مدخلات القطع أيضًا barcode وmaxStockLevel وlocationId وvendorId، كما تقبل quantity/minQuantity/maxStockLevel قيمًا عشرية (مثل 0.5). تُفحص الحقول ذات القيم المحددة: يجب أن تكون priority إحدى LOW, MEDIUM, HIGH, CRITICAL؛ وstatus أمر العمل إحدى OPEN, SCHEDULED, IN_PROGRESS, ON_HOLD, COMPLETED, VALIDATED, CANCELLED, CLOSED؛ وstatus الأصل إحدى OPERATIONAL, DEGRADED, DOWN, MAINTENANCE, STANDBY, RETIRED. وأي قيمة أخرى تُرفض بالرمز BAD_REQUEST. كما تقبل مدخلات القطعة الحقل customReference — رمزكم الخاص للقطعة (رمز نظام ERP لديكم مثلاً)، وهو ما يطابقه بعد ذلك part(barcode:). ولا يُفرض تفرّده: ضمان ذلك يقع عليكم.

مثال: إنشاء أمر عمل

إنشاء أمر عمل: mutation($i: WorkOrderCreateInput!) { createWorkOrder(input: $i) { id title reference status createdAt } } مع المتغيرات {"i": {"title": "Pump #3 leak", "priority": "HIGH"}}

تعديل المخزون

لتغيير كمية قطعة، استخدم adjustStock مع فرق بإشارة — سالب للاستهلاك، موجب للاستلام. يحدّث الكمية ويكتب سطر تدقيق StockMovement في معاملة ذرّية واحدة، ولا يسمح أبدًا بأن ينخفض المخزون دون الصفر. وبالنسبة إلى قطعة يُتابَع مخزونها حسب الموقع، مرّر locationId: تُطبَّق الحركة على ذلك الموقع، ومن دونه يُرفض الاستدعاء: فتكون قيمة errors[0].extensions.code هي BAD_REQUEST، وقيمة extensions.reason هي LOCATION_STOCK_REQUIRES_LOCATION. أما updatePart(quantity) فيعيّن بدلًا من ذلك قيمة مطلقة: ويُرفض على القطعة التي يُتابَع مخزونها حسب الموقع للسبب نفسه، وفي ما عدا ذلك يسجّل الفرق كحركة ADJUSTMENT. يمكنك أيضًا تسجيل مصدر البضاعة: يُحفظ vendorId و documentNumber (رقم فاتورة المورّد أو إذن التسليم، 64 حرفًا كحد أقصى) على الحركة نفسها، فيُظهر السجل من ورّدها وبأي مستند. كلاهما اختياري — الاستهلاك العادي لا يرسل أيًّا منهما. ويجب أن يكون المورّد تابعًا لشركتك.

تسجيل قراءات العدّادات

تسجّل addMeterReading قراءةً على عدّاد — ساعات التشغيل أو عدّاد قطع أو عدّاد مسافة أو مقياس — حتى يغذّي نظام خارجي (SCADA أو بوابة PLC أو ERP) الصيانة الوقائية المعتمدة على العدّادات دون أن يعيد أحد كتابة الرقم يدوياً. ابحث أولاً عن معرّف id العدّاد: query { meters(assetId: 7) { id reference name unit type lastReadingValue lastReadingDate } } ثم أرسل إمّا value (المجموع المطلق الذي يعرضه العدّاد) وإمّا incrementValue (مثلاً ساعات التشغيل منذ آخر قراءة، ويضيفها الخادم إلى القراءة السابقة) — واحداً منهما فقط: mutation { addMeterReading(meterId: 12, value: 1250.5, date: "2026-09-25T06:00:00Z") { id value date isAnomaly } } الحقل date اختياري (ISO 8601، والافتراضي هو الآن) ويمكن أن يكون في الماضي، لا في المستقبل أبداً. في عدّادات RUNTIME وCOUNTER تُرفض القيمة الأقل من القراءة السابقة مع BAD_REQUEST — بعد تصفير العدّاد أو استبداله أعد إرسالها مع confirmRegression: true. تمر القراءة عبر الخدمة نفسها التي تستخدمها شاشة العدّادات في التطبيق: فإن كانت الأحدث، تعمل مشغّلات العدّادات والصيانة الوقائية المعتمدة على العدّادات تماماً كما لو أدخل فنيّ القراءة. تتطلب هذه العملية مفتاحاً بصلاحية FULL.

الإسناد

تُنفَّذ كل عملية تعديل بصفة مالك المفتاح — أي الشخص الذي يعمل المفتاح باسمه — بصلاحياته ونطاق رؤيته للبيانات واسمه في سجل التدقيق. لا يعتمد المفتاح أبدًا خطوة في سير العمل، حتى لو كان مالكه يستطيع ذلك.

إعادة المحاولة بأمان (Idempotency-Key)

انتهاء مهلة الشبكة لا يخبرك إن كانت العملية (mutation) قد نُفِّذت. أرسل الترويسة Idempotency-Key (من 8 إلى 64 حرفًا أو رقمًا أو شرطة — يصلح معرّف UUID) مع كل طلب يكتب بيانات، وأرسل المفتاح نفسه عند إعادة المحاولة. طوال 7 أيام، تُعيد المحاولة بالمفتاح نفسه النتيجة المحفوظة للاستدعاء الأول (تحمل الاستجابة "Idempotent-Replayed: true") بدل تنفيذ العملية مرة أخرى: لا أمر عمل ثانٍ، ولا حركة مخزون مكررة.

  • مفتاح واحد لكل عملية كتابة. في طلب يضم عدة عمليات بأسماء مستعارة، تُسجَّل كل عملية على حدة تحت هذا المفتاح. لا تُقارَن القيم المرسلة: المفتاح نفسه بقيم مختلفة يعيد نتيجة الاستدعاء الأول.
  • الاستدعاء الذي فشل لا يحتفظ بشيء: إعادة محاولته بالمفتاح نفسه تنفّذه من جديد.
  • IDEMPOTENCY_KEY_REUSED — تحت هذا المفتاح، سبق أن نفّذ الاسم نفسه (الحقل أو الاسم المستعار) عملية أخرى، أو استخدم المفتاح مُستدعٍ آخر: استخدم مفتاحًا جديدًا لكل عملية كتابة جديدة.
  • IDEMPOTENCY_IN_PROGRESS — الاستدعاء الأول ما زال قيد التنفيذ: انتظر بضع ثوانٍ ثم أعد المحاولة.

الأخطاء الشائعة

  • GRAPHQL_VALIDATION_FAILED — شكل الإدخال غير مطابق (مثلاً عنوان مطلوب مفقود)
  • BAD_REQUEST — فشل التحقق من قيمة (مثلاً عنوان يتجاوز 500 حرف)
  • FORBIDDEN — خطتك أدنى من Business
  • NOT_FOUND — المعرّف غير موجود أو يعود لشركة أخرى
  • FORBIDDEN — مستوى وصول المفتاح غير كافٍ (مثلاً مفتاح READ يستدعي mutation)

نصيحة

إذا حصلت على خطأ في تسلسل DateTime، فتحقق من مقياس DateTime الخاص بالعميل — يصدر FreeMaint سلاسل ISO 8601.

هل كانت هذه الصفحة مفيدة؟