diff --git a/apps/ops/src/components/customer/LocationCard.vue b/apps/ops/src/components/customer/LocationCard.vue
new file mode 100644
index 0000000..2aa999f
--- /dev/null
+++ b/apps/ops/src/components/customer/LocationCard.vue
@@ -0,0 +1,401 @@
+
+
+
+
+
+
+
+
+
+ loc.connection_type = v.value" />
+ · OLT: loc.olt_port = v.value" />
+ · loc.network_id = v.value" />
+
+
openModal('Service Equipment', eq.name, eq.equipment_type + (eq.brand ? ' — ' + eq.brand : ''))"
+ @add-submission="openWizardForAddress(loc)"
+ @add-service="openAddService(loc)"
+ @add-rebate="openAddService(loc, 'rabais')"
+ @add-equipment="openAddEquipment(loc)"
+ />
+
+
+
+
+ Abonnements ({{ locSubs(loc.name).length }})
+
+ Ajouter un service
+
+
+
+
+
+ Nouvelle soumission
+ Wizard avec étapes
+
+
+
+
+
+ Forfait / Service (direct)
+
+
+
+ Rabais / Crédit
+
+
+
+
+
+
Aucun
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ sub.name }}
+
+ Service F #{{ sub.legacy_service_id }} (corrélé)
+ Ajouté dans ERPNext — absent de F (pas de legacy_service_id)
+ {{ subMainLabel(sub) }}
+
+
+
+
+
+ {{ displayValue }}
+
+
+
+
+
+
+
+
+ Créer un ticket pour ce service
+
+
+ {{ sub.billing_frequency === 'A' ? 'Passer mensuel' : 'Passer annuel' }}
+
+
+ {{ sub.status === 'Cancelled' ? 'Réactiver' : 'Désactiver' }}
+
+
+ Supprimer définitivement
+
+
+
+
+
+
+
+
+
+
+ Total mensuel
+ {{ formatMoney(locSubsMonthlyTotal(loc.name)) }} /mois
+
+
+
+
+ Total récurrent négatif — les rabais dépassent les frais. Vérifier/corriger les rabais dans la facturation (F).
+
+
+
+
+
+ Abonnements annuels ({{ locSubsAnnual(loc.name).length }})
+
+
+
+
+
+ {{ sub.name }}
+ A
+ {{ subMainLabel(sub) }}
+ {{ formatMoney(annualPrice(sub)) }}
+
+
+ (/m×12)
+
+
+
+
+
+
+ {{ Number(sub.cancel_at_period_end) ? 'Non récurrent' : 'Récurrent' }}
+
+
+
+
+
+ Prochaine: {{ formatDate(sub.current_invoice_start) }}
+
+
+
+
+
+ Passer mensuel
+
+
+ {{ sub.status === 'Cancelled' ? 'Réactiver' : 'Désactiver' }}
+
+
+ Supprimer définitivement
+
+
+
+
+
+
+ Total annuel
+ {{ formatMoney(locSubsAnnualTotal(loc.name)) }} /an
+
+
+
+
+
+
+ Tickets ({{ locTickets(loc.name).length }})
+
+ Créer un ticket pour cette adresse
+
+
+
+
+
+
+ {{ t.legacy_ticket_id || t.name }}
+ {{ t.subject }}
+
+
+
+ {{ staffInitials(t.opened_by_staff) }}
+ {{ t.opened_by_staff }}
+
+
+ {{ staffInitials(t.assigned_staff) }}
+ {{ t.assigned_staff }}
+
+
+
{{ t.opening_date }}
+
{{ t.status }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/ops/src/pages/ClientDetailPage.vue b/apps/ops/src/pages/ClientDetailPage.vue
index 7027980..afcef0d 100644
--- a/apps/ops/src/pages/ClientDetailPage.vue
+++ b/apps/ops/src/pages/ClientDetailPage.vue
@@ -70,280 +70,38 @@
Aucun lieu de service
-
-
-
-
-
-
-
-
- loc.connection_type = v.value" />
- · OLT: loc.olt_port = v.value" />
- · loc.network_id = v.value" />
-
-
openModal('Service Equipment', eq.name, eq.equipment_type + (eq.brand ? ' — ' + eq.brand : ''))"
- @add-submission="openWizardForAddress(loc)"
- @add-service="openAddService(loc)"
- @add-rebate="openAddService(loc, 'rabais')"
- @add-equipment="openAddEquipment(loc)"
- />
-
-
-
-
- Abonnements ({{ locSubs(loc.name).length }})
-
- Ajouter un service
-
-
-
-
-
- Nouvelle soumission
- Wizard avec étapes
-
-
-
-
-
- Forfait / Service (direct)
-
-
-
- Rabais / Crédit
-
-
-
-
-
-
Aucun
-
-
-
-
-
-
-
-
-
-
-
-
- {{ sub.name }}
-
- Service F #{{ sub.legacy_service_id }} (corrélé)
- Ajouté dans ERPNext — absent de F (pas de legacy_service_id)
- {{ subMainLabel(sub) }}
-
-
-
-
-
- {{ displayValue }}
-
-
-
-
-
-
-
- l.name === loc.name), sub)">
- Créer un ticket pour ce service
-
-
- {{ sub.billing_frequency === 'A' ? 'Passer mensuel' : 'Passer annuel' }}
-
-
- {{ sub.status === 'Cancelled' ? 'Réactiver' : 'Désactiver' }}
-
-
- Supprimer définitivement
-
-
-
-
-
-
-
-
-
-
- Total mensuel
- {{ formatMoney(locSubsMonthlyTotal(loc.name)) }} /mois
-
-
-
-
- Total récurrent négatif — les rabais dépassent les frais. Vérifier/corriger les rabais dans la facturation (F).
-
-
-
-
-
- Abonnements annuels ({{ locSubsAnnual(loc.name).length }})
-
-
-
-
-
- {{ sub.name }}
- A
- {{ subMainLabel(sub) }}
- {{ formatMoney(annualPrice(sub)) }}
-
-
- (/m×12)
-
-
-
-
-
-
- {{ Number(sub.cancel_at_period_end) ? 'Non récurrent' : 'Récurrent' }}
-
-
-
-
-
- Prochaine: {{ formatDate(sub.current_invoice_start) }}
-
-
-
-
-
- Passer mensuel
-
-
- {{ sub.status === 'Cancelled' ? 'Réactiver' : 'Désactiver' }}
-
-
- Supprimer définitivement
-
-
-
-
-
-
- Total annuel
- {{ formatMoney(locSubsAnnualTotal(loc.name)) }} /an
-
-
-
-
-
-
- Tickets ({{ locTickets(loc.name).length }})
-
- Créer un ticket pour cette adresse
-
-
-
-
-
-
- {{ t.legacy_ticket_id || t.name }}
- {{ t.subject }}
-
-
-
- {{ staffInitials(t.opened_by_staff) }}
- {{ t.opened_by_staff }}
-
-
- {{ staffInitials(t.assigned_staff) }}
- {{ t.assigned_staff }}
-
-
-
{{ t.opening_date }}
-
{{ t.status }}
-
-
-
-
-
-
+
@@ -1061,9 +819,9 @@ import { authFetch } from 'src/api/auth'
import { BASE_URL, ERP_DESK_URL } from 'src/config/erpnext'
import { HUB_URL } from 'src/config/hub'
import { formatDate, formatDateTime, formatMoney, staffColor, staffInitials, noteAuthorName } from 'src/composables/useFormatters'
-import { locStatusClass, ticketStatusClass, invStatusClass, priorityClass } from 'src/composables/useStatusClasses'
+import { ticketStatusClass, invStatusClass, priorityClass } from 'src/composables/useStatusClasses'
import { useDetailModal } from 'src/composables/useDetailModal'
-import { useSubscriptionGroups, isRebate, subMainLabel, sectionTotal, annualPrice } from 'src/composables/useSubscriptionGroups'
+import { useSubscriptionGroups } from 'src/composables/useSubscriptionGroups'
import { useSubscriptionActions } from 'src/composables/useSubscriptionActions'
import { useCustomerNotes } from 'src/composables/useCustomerNotes'
import { useConversations } from 'src/composables/useConversations'
@@ -1085,11 +843,11 @@ import { usePermissions } from 'src/composables/usePermissions'
import { usePaymentActions } from 'src/composables/usePaymentActions'
import { useClientData } from 'src/composables/useClientData'
import { useEquipmentActions } from 'src/composables/useEquipmentActions'
-import { locInlineFields, equipTypeOptions, equipStatusOptions, defaultSectionsOpen, phoneLabelMap, CLIENT_360_MODULES } from 'src/data/client-constants'
+import { equipTypeOptions, equipStatusOptions, defaultSectionsOpen, phoneLabelMap, CLIENT_360_MODULES } from 'src/data/client-constants'
import { useUserPrefs } from 'src/composables/useUserPrefs'
import { erpPdfUrl } from 'src/utils/erp-pdf'
import RewardDialog from 'src/components/customer/RewardDialog.vue'
-import DeviceStrip from 'src/components/customer/DeviceStrip.vue'
+import LocationCard from 'src/components/customer/LocationCard.vue'
const $q = useQuasar()
const { can } = usePermissions()