Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 33 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,42 @@ Each layer depends only on the layer(s) to its left:
- **Providers** (`Resgrid.Providers.*`): External integrations — depends on Model
- **Web/Workers**: Entry points — depend on everything

### Dependency Injection (Autofac + Service Locator)
### Dependency Injection (Autofac)

This codebase uses **Service Locator** pattern, NOT constructor injection:
**Constructor injection is the convention.** Services, repositories, providers, controllers
(MVC and v4 API), and hubs all declare their dependencies as constructor parameters and let
Autofac supply them. When a type needs a new dependency, add a constructor parameter — do NOT
reach for the service locator. Existing constructors are large (e.g. `CommunicationTestService`
takes 18 parameters, `DispatchController` 30); that is deliberate and expected, and it is what
keeps these types unit-testable with mocks.

```csharp
// How services are resolved throughout the codebase:
var service = Bootstrapper.GetKernel().Resolve<ISomeService>();
// The convention — constructor injection:
public class SomethingService : ISomethingService
{
private readonly IDepartmentsService _departmentsService;

public SomethingService(IDepartmentsService departmentsService)
{
_departmentsService = departmentsService;
}
}
```

**Service Locator is the exception, not the rule.** `Bootstrapper.GetKernel().Resolve<T>()` is
reserved for the specific places where no DI container is available at the call site or where a
container-managed constructor cannot be used:

- Worker logic under `Workers/Resgrid.Workers.Framework/Logic/` (queue consumers constructed by
the job host, not by Autofac).
- Static helpers and extension methods that have no constructor to inject into.
- Deliberate lazy escapes from a construction-time dependency cycle — and even then prefer
`Lazy<T>` as a constructor parameter (see `CallsService`'s `Lazy<IProtectedWriteService>`)
over a service-locator call.

If you find yourself adding `Bootstrapper.GetKernel().Resolve<T>()` anywhere else, use a
constructor parameter instead.

The `Bootstrapper` class (in `Resgrid.Workers.Framework/Bootstrapper.cs`) initializes Autofac with module-based registration:
```csharp
var builder = new ContainerBuilder();
Expand Down Expand Up @@ -193,9 +220,9 @@ Task type discrimination uses `(int)TaskTypes.SomeEnum`.

When Billing API is configured but returns a response where `Data.Data` is null, `GetCurrentPlanForDepartmentAsync` returns null instead of the free plan fallback. Callers that access `plan.PlanId` or `plan.GetLimitForTypeAsInt()` will NRE.

### 3. Service Locator in Constructors
### 3. Injected Dependencies Are Never Null

Unlike modern DI, this codebase resolves dependencies explicitly in constructors via `Bootstrapper.GetKernel().Resolve<T>()`. When examining stack traces, dependencies are never null due to constructor injection failures — the Bootstrapper would fail at app start. If a NullReferenceException occurs on a service call, the issue is typically in the return value of the called method, not the service reference itself.
Dependencies come from Autofac constructor injection, so they are never null at a call site — a missing registration fails at container build (app start), not at the point of use. If a NullReferenceException occurs on a service call, the issue is almost always in the **return value** of the called method, not the service reference itself. The same holds for the worker paths that use `Bootstrapper.GetKernel().Resolve<T>()`: an unregistered type throws a resolution exception rather than handing back null.

### 4. Async State Machine Line Numbers

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -691,4 +691,7 @@
<data name="MaintenanceUnavailable" xml:space="preserve"><value>تخزين الصيانة غير متاح.</value></data>
<data name="PermissionRequired" xml:space="preserve"><value>ليس لديك إذن لتنفيذ هذا الإجراء.</value></data>
<data name="TargetUnavailable" xml:space="preserve"><value>اختر هدفًا متاحًا في قسمك.</value></data>
<data name="HistoricalWorkOrdersUnavailable" xml:space="preserve"><value>تعذّر إعادة بناء بعض الحالات التاريخية لأوامر العمل. لا تدّعي الحزمة تغطية الصيانة بالكامل.</value></data>
<data name="RestrictedWorkOrders" xml:space="preserve"><value>تقتصر أدلة أوامر العمل على النطاق المصرّح للقارئ بالوصول إليه حاليًا.</value></data>
<data name="ProtectedDataRequired" xml:space="preserve"><value>يجب إلغاء قفل البيانات المحمية للمتابعة.</value></data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -691,4 +691,7 @@
<data name="MaintenanceUnavailable" xml:space="preserve"><value>Wartungsspeicher nicht verfügbar.</value></data>
<data name="PermissionRequired" xml:space="preserve"><value>Sie haben keine Berechtigung für diese Aktion.</value></data>
<data name="TargetUnavailable" xml:space="preserve"><value>Wählen Sie ein verfügbares Ziel in Ihrer Organisation.</value></data>
<data name="HistoricalWorkOrdersUnavailable" xml:space="preserve"><value>Einige historische Arbeitsauftragszustände konnten nicht rekonstruiert werden. Das Paket beansprucht keine vollständige Wartungsabdeckung.</value></data>
<data name="RestrictedWorkOrders" xml:space="preserve"><value>Arbeitsauftragsnachweise sind auf den aktuell berechtigten Lesebereich beschränkt.</value></data>
<data name="ProtectedDataRequired" xml:space="preserve"><value>Geschützte Daten müssen zum Fortfahren entsperrt werden.</value></data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -691,4 +691,7 @@
<data name="MaintenanceUnavailable" xml:space="preserve"><value>Ο χώρος αποθήκευσης συντήρησης δεν είναι διαθέσιμος.</value></data>
<data name="PermissionRequired" xml:space="preserve"><value>Δεν έχετε άδεια για αυτή την ενέργεια.</value></data>
<data name="TargetUnavailable" xml:space="preserve"><value>Επιλέξτε έναν διαθέσιμο στόχο στο τμήμα σας.</value></data>
<data name="HistoricalWorkOrdersUnavailable" xml:space="preserve"><value>Δεν ήταν δυνατή η ανακατασκευή ορισμένων ιστορικών καταστάσεων εντολών. Το πακέτο δεν εγγυάται πλήρη κάλυψη συντήρησης.</value></data>
<data name="RestrictedWorkOrders" xml:space="preserve"><value>Τα τεκμήρια εντολών περιορίζονται στο τρέχον εξουσιοδοτημένο πεδίο πρόσβασης του αναγνώστη.</value></data>
<data name="ProtectedDataRequired" xml:space="preserve"><value>Τα προστατευμένα δεδομένα πρέπει να ξεκλειδωθούν για να συνεχίσετε.</value></data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -691,4 +691,7 @@
<data name="MaintenanceUnavailable" xml:space="preserve"><value>Maintenance storage is unavailable.</value></data>
<data name="PermissionRequired" xml:space="preserve"><value>You do not have permission to perform this action.</value></data>
<data name="TargetUnavailable" xml:space="preserve"><value>Select an available target in your department.</value></data>
<data name="HistoricalWorkOrdersUnavailable" xml:space="preserve"><value>Some historical work-order states could not be reconstructed. The packet does not claim complete maintenance coverage.</value></data>
<data name="RestrictedWorkOrders" xml:space="preserve"><value>Work-order evidence is limited to the reader’s current authorized scope.</value></data>
<data name="ProtectedDataRequired" xml:space="preserve"><value>Protected data must be unlocked to continue.</value></data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -691,4 +691,7 @@
<data name="MaintenanceUnavailable" xml:space="preserve"><value>El almacenamiento de mantenimiento no está disponible.</value></data>
<data name="PermissionRequired" xml:space="preserve"><value>No tiene permiso para realizar esta acción.</value></data>
<data name="TargetUnavailable" xml:space="preserve"><value>Seleccione un destino disponible en su departamento.</value></data>
<data name="HistoricalWorkOrdersUnavailable" xml:space="preserve"><value>No se pudieron reconstruir algunos estados históricos de las órdenes. El paquete no garantiza una cobertura completa del mantenimiento.</value></data>
<data name="RestrictedWorkOrders" xml:space="preserve"><value>Las pruebas de las órdenes se limitan al ámbito autorizado actual del lector.</value></data>
<data name="ProtectedDataRequired" xml:space="preserve"><value>Debe desbloquear los datos protegidos para continuar.</value></data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -691,4 +691,7 @@
<data name="MaintenanceUnavailable" xml:space="preserve"><value>Le stockage de maintenance est indisponible.</value></data>
<data name="PermissionRequired" xml:space="preserve"><value>Vous n’avez pas l’autorisation d’effectuer cette action.</value></data>
<data name="TargetUnavailable" xml:space="preserve"><value>Sélectionnez une cible disponible dans votre service.</value></data>
<data name="HistoricalWorkOrdersUnavailable" xml:space="preserve"><value>Certains états historiques des ordres n’ont pas pu être reconstitués. Le dossier ne garantit pas une couverture complète de la maintenance.</value></data>
<data name="RestrictedWorkOrders" xml:space="preserve"><value>Les preuves des ordres sont limitées au périmètre actuellement autorisé du lecteur.</value></data>
<data name="ProtectedDataRequired" xml:space="preserve"><value>Les données protégées doivent être déverrouillées pour continuer.</value></data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -691,4 +691,7 @@
<data name="MaintenanceUnavailable" xml:space="preserve"><value>L'archivio di manutenzione non è disponibile.</value></data>
<data name="PermissionRequired" xml:space="preserve"><value>Non hai l’autorizzazione per eseguire questa azione.</value></data>
<data name="TargetUnavailable" xml:space="preserve"><value>Seleziona un destinatario disponibile nel tuo dipartimento.</value></data>
<data name="HistoricalWorkOrdersUnavailable" xml:space="preserve"><value>Non è stato possibile ricostruire alcuni stati storici degli ordini. Il pacchetto non garantisce una copertura completa della manutenzione.</value></data>
<data name="RestrictedWorkOrders" xml:space="preserve"><value>Le prove degli ordini sono limitate all’ambito attualmente autorizzato del lettore.</value></data>
<data name="ProtectedDataRequired" xml:space="preserve"><value>È necessario sbloccare i dati protetti per continuare.</value></data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -691,4 +691,7 @@
<data name="MaintenanceUnavailable" xml:space="preserve"><value>Magazyn danych konserwacji jest niedostępny.</value></data>
<data name="PermissionRequired" xml:space="preserve"><value>Nie masz uprawnień do wykonania tej czynności.</value></data>
<data name="TargetUnavailable" xml:space="preserve"><value>Wybierz dostępny obiekt w swoim dziale.</value></data>
<data name="HistoricalWorkOrdersUnavailable" xml:space="preserve"><value>Nie udało się odtworzyć niektórych historycznych stanów zleceń. Pakiet nie gwarantuje pełnego pokrycia prac konserwacyjnych.</value></data>
<data name="RestrictedWorkOrders" xml:space="preserve"><value>Dowody zleceń są ograniczone do bieżącego zakresu uprawnień odbiorcy.</value></data>
<data name="ProtectedDataRequired" xml:space="preserve"><value>Aby kontynuować, należy odblokować dane chronione.</value></data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -691,4 +691,7 @@
<data name="MaintenanceUnavailable" xml:space="preserve"><value>Maintenance storage is unavailable.</value></data>
<data name="PermissionRequired" xml:space="preserve"><value>You do not have permission to perform this action.</value></data>
<data name="TargetUnavailable" xml:space="preserve"><value>Select an available target in your department.</value></data>
<data name="HistoricalWorkOrdersUnavailable" xml:space="preserve"><value>Some historical work-order states could not be reconstructed. The packet does not claim complete maintenance coverage.</value></data>
<data name="RestrictedWorkOrders" xml:space="preserve"><value>Work-order evidence is limited to the reader’s current authorized scope.</value></data>
<data name="ProtectedDataRequired" xml:space="preserve"><value>Protected data must be unlocked to continue.</value></data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -691,4 +691,7 @@
<data name="MaintenanceUnavailable" xml:space="preserve"><value>Underhållslagringen är inte tillgänglig.</value></data>
<data name="PermissionRequired" xml:space="preserve"><value>Du har inte behörighet att utföra denna åtgärd.</value></data>
<data name="TargetUnavailable" xml:space="preserve"><value>Välj ett tillgängligt mål inom din avdelning.</value></data>
<data name="HistoricalWorkOrdersUnavailable" xml:space="preserve"><value>Vissa historiska tillstånd för arbetsorder kunde inte återskapas. Paketet garanterar inte fullständig underhållstäckning.</value></data>
<data name="RestrictedWorkOrders" xml:space="preserve"><value>Arbetsorderbevisen begränsas till läsarens nuvarande behörighet.</value></data>
<data name="ProtectedDataRequired" xml:space="preserve"><value>Skyddade data måste låsas upp för att fortsätta.</value></data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -691,4 +691,7 @@
<data name="MaintenanceUnavailable" xml:space="preserve"><value>Сховище технічного обслуговування недоступне.</value></data>
<data name="PermissionRequired" xml:space="preserve"><value>Ви не маєте дозволу на цю дію.</value></data>
<data name="TargetUnavailable" xml:space="preserve"><value>Виберіть доступний об’єкт у своєму підрозділі.</value></data>
<data name="HistoricalWorkOrdersUnavailable" xml:space="preserve"><value>Деякі історичні стани нарядів не вдалося відновити. Пакет не гарантує повного охоплення технічного обслуговування.</value></data>
<data name="RestrictedWorkOrders" xml:space="preserve"><value>Докази нарядів обмежені поточним дозволеним обсягом доступу читача.</value></data>
<data name="ProtectedDataRequired" xml:space="preserve"><value>Щоб продовжити, потрібно розблокувати захищені дані.</value></data>
</root>
46 changes: 46 additions & 0 deletions Core/Resgrid.Localization/Areas/User/Records/Records.ar.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1946,4 +1946,50 @@
<data name="AnalyticsWentOverdue" xml:space="preserve"><value>أصبحت متأخرة خلال الفترة</value></data>
<data name="AnalyticsWithinTarget" xml:space="preserve"><value>ضمن الهدف</value></data>
<data name="AnalyticsWorkload" xml:space="preserve"><value>عبء العمل</value></data>
<data name="ReadinessAssets" xml:space="preserve"><value>الأصول</value></data>
<data name="ReadinessAssetsAssigned" xml:space="preserve"><value>الأصول المخصصة</value></data>
<data name="ReadinessByStatus" xml:space="preserve"><value>حسب الحالة</value></data>
<data name="ReadinessChecklists" xml:space="preserve"><value>الالتزام بقوائم الفحص</value></data>
<data name="ReadinessChecks" xml:space="preserve"><value>الفحوصات</value></data>
<data name="ReadinessChecksExpected" xml:space="preserve"><value>المتوقعة</value></data>
<data name="ReadinessCohort" xml:space="preserve"><value>الفترة المقاسة</value></data>
<data name="ReadinessCompletionRate" xml:space="preserve"><value>معدل الإكمال</value></data>
<data name="ReadinessCosts" xml:space="preserve"><value>التكاليف</value></data>
<data name="ReadinessCoverage" xml:space="preserve"><value>التغطية</value></data>
<data name="ReadinessDamaged" xml:space="preserve"><value>تالفة</value></data>
<data name="ReadinessEmergency" xml:space="preserve"><value>طارئة</value></data>
<data name="ReadinessEquipment" xml:space="preserve"><value>المعدات</value></data>
<data name="ReadinessEvidence" xml:space="preserve"><value>حزم الجاهزية</value></data>
<data name="ReadinessExpiringSoon" xml:space="preserve"><value>تنتهي خلال 30 يومًا</value></data>
<data name="ReadinessForbidden" xml:space="preserve"><value>رفضت الوحدة هذا العرض لحسابك؛ لم يُحتسب شيء منها.</value></data>
<data name="ReadinessHeader" xml:space="preserve"><value>الجاهزية</value></data>
<data name="ReadinessIntro" xml:space="preserve"><value>جاهزية المركبات والمعدات مجمّعة من وحدات قوائم الفحص والصيانة والمخزون. كل وحدة تتحقق من صلاحيتك بنفسها؛ تضيف السجلات فقط حزم الجاهزية التي التقطتها.</value></data>
<data name="ReadinessIssuancesOutstanding" xml:space="preserve"><value>عمليات الصرف المعلقة</value></data>
<data name="ReadinessIssued" xml:space="preserve"><value>مصروفة</value></data>
<data name="ReadinessLabor" xml:space="preserve"><value>العمالة</value></data>
<data name="ReadinessMissed" xml:space="preserve"><value>الفائتة</value></data>
<data name="ReadinessMissingRepairTimes" xml:space="preserve"><value>بدون أوقات</value></data>
<data name="ReadinessMttr" xml:space="preserve"><value>متوسط وقت الإصلاح (س)</value></data>
<data name="ReadinessNeedsAttention" xml:space="preserve"><value>تحتاج إلى اهتمام</value></data>
<data name="ReadinessOnHold" xml:space="preserve"><value>معلّقة</value></data>
<data name="ReadinessOnTime" xml:space="preserve"><value>في الوقت المحدد</value></data>
<data name="ReadinessOnTimeRate" xml:space="preserve"><value>معدل الالتزام بالوقت</value></data>
<data name="ReadinessOpenByAge" xml:space="preserve"><value>المفتوحة حسب العمر</value></data>
<data name="ReadinessOpenByPriority" xml:space="preserve"><value>المفتوحة حسب الأولوية</value></data>
<data name="ReadinessOutForRepair" xml:space="preserve"><value>قيد الإصلاح</value></data>
<data name="ReadinessOverdueReturns" xml:space="preserve"><value>إرجاعات متأخرة</value></data>
<data name="ReadinessPackets" xml:space="preserve"><value>الحزم</value></data>
<data name="ReadinessPacketsCaptured" xml:space="preserve"><value>الحزم الملتقطة</value></data>
<data name="ReadinessParts" xml:space="preserve"><value>القطع</value></data>
<data name="ReadinessRecordsWithPacket" xml:space="preserve"><value>سجلات بحزمة</value></data>
<data name="ReadinessRedacted" xml:space="preserve"><value>تُحجب تفاصيل الإجابات لمستوى وصولك؛ الأعداد كاملة.</value></data>
<data name="ReadinessRepairSamples" xml:space="preserve"><value>إصلاحات موقوتة</value></data>
<data name="ReadinessSkipped" xml:space="preserve"><value>متجاوزة</value></data>
<data name="ReadinessState" xml:space="preserve"><value>الحالة</value></data>
<data name="ReadinessTrend" xml:space="preserve"><value>الفحوصات المتوقعة والفائتة حسب اليوم</value></data>
<data name="ReadinessUnavailableSources" xml:space="preserve"><value>مصدر غير متاح</value></data>
<data name="ReadinessUnit" xml:space="preserve"><value>الوحدة</value></data>
<data name="ReadinessUnits" xml:space="preserve"><value>لوحة جاهزية الوحدات</value></data>
<data name="ReadinessUnknownCosts" xml:space="preserve"><value>بنود بدون سعر</value></data>
<data name="ReadinessWorkOrders" xml:space="preserve"><value>أوامر العمل</value></data>
</root>
Loading
Loading