diff --git a/apps/ops/src/components/customer/CustomerHeader.vue b/apps/ops/src/components/customer/CustomerHeader.vue index 996ee99..7974a2a 100644 --- a/apps/ops/src/components/customer/CustomerHeader.vue +++ b/apps/ops/src/components/customer/CustomerHeader.vue @@ -34,14 +34,6 @@ :input-style="{ color: statusColor }" style="min-width:100px;max-width:140px;text-align:right" @update:model-value="saveStatus" /> -
- - -
@@ -75,7 +67,6 @@ diff --git a/apps/ops/src/components/shared/HelpHint.vue b/apps/ops/src/components/shared/HelpHint.vue new file mode 100644 index 0000000..03aa6c6 --- /dev/null +++ b/apps/ops/src/components/shared/HelpHint.vue @@ -0,0 +1,63 @@ + + + + + diff --git a/apps/ops/src/components/shared/OrchestratorDialog.vue b/apps/ops/src/components/shared/OrchestratorDialog.vue index 36e3734..58c00d5 100644 --- a/apps/ops/src/components/shared/OrchestratorDialog.vue +++ b/apps/ops/src/components/shared/OrchestratorDialog.vue @@ -3,7 +3,7 @@ -
Commande dictée
+
{{ title }}
@@ -22,6 +22,9 @@ + +
{{ reply }}
+
@@ -72,6 +75,9 @@
+ +
{{ a.preview }}
+ Action conséquente
+ + diff --git a/apps/ops/src/components/shared/UserProfileDialog.vue b/apps/ops/src/components/shared/UserProfileDialog.vue new file mode 100644 index 0000000..fed955c --- /dev/null +++ b/apps/ops/src/components/shared/UserProfileDialog.vue @@ -0,0 +1,128 @@ + + + diff --git a/apps/ops/src/components/shared/detail-sections/IssueDetail.vue b/apps/ops/src/components/shared/detail-sections/IssueDetail.vue index 0a58a8f..45aa655 100644 --- a/apps/ops/src/components/shared/detail-sections/IssueDetail.vue +++ b/apps/ops/src/components/shared/detail-sections/IssueDetail.vue @@ -1,15 +1,14 @@ @@ -344,7 +339,7 @@
Aucune conversation (courriel / SMS) liée à ce client.
- + {{ commsTitle(d) }} @@ -356,6 +351,9 @@ +
+ {{ msgOpen ? '▲ Réduire' : '▼ Voir ' + (commsDiscussions.length - 1) + ' de plus' }} +
@@ -366,7 +364,7 @@
Aucune activité récente.
- + {{ r.label }} @@ -375,8 +373,8 @@ {{ formatDate(r.date) }} -
- {{ recentOpen ? '▲ Réduire' : '▼ Voir ' + (activityLog.length - 5) + ' de plus' }} +
+ {{ recentOpen ? '▲ Réduire' : '▼ Voir ' + (activityLog.length - 1) + ' de plus' }}
@@ -391,7 +389,7 @@ Enregistrer la note (Ctrl/⌘+Entrée)
Aucune note
-
+
{{ noteAuthorName(c.comment_by || c.owner) }} @@ -400,6 +398,9 @@
{{ c.content }}
+
+ {{ notesOpen ? '▲ Réduire' : '▼ Voir ' + (sortedComments.length - 1) + ' de plus' }} +
@@ -598,7 +599,7 @@ :initial-address="findSlotAddr" :initial-skill="findSlotSkill" @select="onFindSlotSelected" /> + :address="findSlotAddr" :connection-type="primaryLocation()?.connection_type || ''" @book="onAvailBook" /> @@ -612,6 +613,7 @@ :doc-name="modalDocName" :title="modalTitle" :doc="modalDoc" :comments="modalComments" :comms="modalComms" :files="modalFiles" :doc-fields="modalDocFields" :dispatch-jobs="modalDispatchJobs" + :hide-customer-link="true" @navigate="(dt, name, t) => openModal(dt, name, t)" @open-pdf="openPdf" @save-field="saveSubField" @toggle-recurring="toggleRecurringModal" @dispatch-created="onDispatchCreated" @@ -677,7 +679,33 @@ - + + +
+ + + + + + + +
Enregistrer un paiement
+ +
+ +
Paiement général de {{ customer?.customer_name }} (à l'avance / non alloué à une facture précise — crédit sur le compte client).
+ + + +
+ +
Comptabilisé via ERPNext — dépôt {{ onAcctDlg.preview.paid_to }} ; crédit non alloué sur le compte client.
+
+
{{ onAcctDlg.error }}
+
+ + +
@@ -691,7 +719,7 @@ import { Notify, useQuasar } from 'quasar' import draggable from 'vuedraggable' import { deleteDoc, createDoc, listDocs } from 'src/api/erp' import { authFetch } from 'src/api/auth' -import { BASE_URL, ERP_DESK_URL } from 'src/config/erpnext' +import { BASE_URL } from 'src/config/erpnext' import { HUB_URL } from 'src/config/hub' import { formatDate, formatDateTime, formatMoney, noteAuthorName } from 'src/composables/useFormatters' import { invStatusClass } from 'src/composables/useStatusClasses' @@ -712,6 +740,7 @@ import { useSoftphone } from 'src/composables/useSoftphone' import UnifiedCreateModal from 'src/components/shared/UnifiedCreateModal.vue' import SuggestSlotsDialog from 'src/modules/dispatch/components/SuggestSlotsDialog.vue' // « Trouver un créneau » direct depuis la fiche (appel/réparation) import AvailabilityByReason from 'src/components/shared/AvailabilityByReason.vue' // raison → compétence → disponibilité (dénominateur filtré) +import OverflowMenu from 'src/components/shared/OverflowMenu.vue' // ⋮ regroupe Récompense + Personnaliser (déclutter en-tête) import { DEPARTMENTS } from 'src/config/departments' import FlowQuickButton from 'src/components/flow-editor/FlowQuickButton.vue' import CreateInvoiceModal from 'src/components/shared/CreateInvoiceModal.vue' @@ -1287,11 +1316,33 @@ function commsSnippet (d) { const openTicketCount = computed(() => tickets.value.filter(t => t.status === 'Open').length) // Affiche 5 tickets par défaut ; « Voir tous » révèle le reste (déjà chargés) ou les recharge tous. const visibleTickets = computed(() => ticketsExpanded.value ? tickets.value : tickets.value.slice(0, 5)) -// Inscrire un paiement reçu : ouvre le formulaire Payment Entry d'ERPNext pré-rempli (accounts/validation gérés par ERPNext — pas de doc financier fabriqué à la main). +// Inscrire un paiement à l'avance / non alloué (SANS facture) — dialogue NATIF (hub /payments/record-on-account), +// remplace l'ancien lien vers le formulaire Payment Entry d'ERPNext desk. +const onAcctDlg = reactive({ open: false, amount: 0, mode: 'Cheque', referenceNo: '', loading: false, submitting: false, preview: null, error: '' }) function recordPayment () { if (!customer.value) return - const url = ERP_DESK_URL + '/app/payment-entry/new?payment_type=Receive&party_type=Customer&party=' + encodeURIComponent(customer.value.name) - window.open(url, '_blank', 'noopener') + onAcctDlg.amount = 0; onAcctDlg.mode = 'Cheque'; onAcctDlg.referenceNo = ''; onAcctDlg.preview = null; onAcctDlg.error = '' + onAcctDlg.open = true +} +async function onAcctReq (preview) { + return fetch(`${HUB_URL}/payments/record-on-account`, { + method: 'POST', headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ customer: customer.value.name, amount: Number(onAcctDlg.amount) || undefined, mode_of_payment: onAcctDlg.mode, reference_no: onAcctDlg.referenceNo || undefined, preview: !!preview }), + }).then(r => r.json().then(d => ({ ok: r.ok, d }))) +} +async function onAcctPreview () { + if (!(onAcctDlg.amount > 0)) { onAcctDlg.preview = null; return } + onAcctDlg.loading = true; onAcctDlg.error = '' + try { const { ok, d } = await onAcctReq(true); if (!ok || d.error) { onAcctDlg.error = d.error || 'Erreur'; onAcctDlg.preview = null } else onAcctDlg.preview = d } + catch (e) { onAcctDlg.error = e.message } finally { onAcctDlg.loading = false } +} +async function onAcctConfirm () { + onAcctDlg.submitting = true; onAcctDlg.error = '' + try { + const { ok, d } = await onAcctReq(false) + if (!ok || d.error) { onAcctDlg.error = d.error || 'Échec de l\'enregistrement' } + else { $q.notify({ type: 'positive', message: `Paiement de ${formatMoney(d.paid_amount)} enregistré (${d.name})` }); onAcctDlg.open = false; loadCustomer(props.id) } + } catch (e) { onAcctDlg.error = e.message } finally { onAcctDlg.submitting = false } } // « Enregistrer un paiement » par facture (modèle Odoo, sans quitter l'app) : aperçu ERPNext → confirmation → écriture. @@ -1332,7 +1383,9 @@ const kpi360 = computed(() => [ { key: 'subs', icon: 'router', color: 'blue-6', label: 'Abonnements actifs', value: activeSubsCount.value }, { key: 'balance', icon: 'account_balance_wallet', color: totalOutstanding.value > 0 ? 'red-6' : 'green-7', label: 'Solde', value: formatMoney(totalOutstanding.value) }, ]) -const recentOpen = ref(false) // Journal d'activité (droite) : 5 par défaut, « Voir N de plus / Réduire » +const recentOpen = ref(false) // Journal d'activité (droite) : dernier seulement, « Voir N de plus / Réduire » +const msgOpen = ref(false) // Messagerie (droite) : dernière conversation seulement +const notesOpen = ref(false) // Notes internes (droite) : dernière note seulement // Journal = tickets + interventions + appels/comms loggées (Communication ERPNext). Les courriels/SMS vivent dans le panneau « Messagerie » → PAS re-dupliqués ici. const activityLog = computed(() => { const items = [] diff --git a/apps/ops/src/pages/DashboardPage.vue b/apps/ops/src/pages/DashboardPage.vue index 4136871..579695b 100644 --- a/apps/ops/src/pages/DashboardPage.vue +++ b/apps/ops/src/pages/DashboardPage.vue @@ -53,7 +53,7 @@
-
Planificateur
+
Planificateur
{{ schedulerEnabled ? 'Actif' : 'Désactivé' }} @@ -71,7 +71,14 @@
-
Facturation récurrente
+
Facturation récurrente +
    +
  • 1. Générer — crée les factures brouillon des abonnements actifs.
  • +
  • 2. Soumettre — valide les brouillons (factures officielles).
  • +
  • 3. PPA — prélève les cartes enregistrées.
  • +
+ ⚠ Le PPA prélève immédiatement : à lancer une seule fois par cycle (un double lancement peut charger deux fois). +
@@ -207,6 +214,7 @@ import { getCapacity } from 'src/api/roster' import { BASE_URL } from 'src/config/erpnext' import { HUB_SSE_URL } from 'src/config/dispatch' import { startOfWeek, localDateStr } from 'src/composables/useHelpers' +import HelpHint from 'src/components/shared/HelpHint.vue' import OutageAlertsPanel from 'src/components/shared/OutageAlertsPanel.vue' import OccupancyStrip from 'src/components/shared/OccupancyStrip.vue' import DisclosureSection from 'src/components/shared/DisclosureSection.vue' diff --git a/apps/ops/src/pages/EquipePage.vue b/apps/ops/src/pages/EquipePage.vue index a16c852..5f054a7 100644 --- a/apps/ops/src/pages/EquipePage.vue +++ b/apps/ops/src/pages/EquipePage.vue @@ -22,15 +22,64 @@ flat dense class="ops-table" :loading="loading" :pagination="{ rowsPerPage: 20 }" - /> + > + + + +
+ + diff --git a/apps/ops/src/pages/EvaluationsPage.vue b/apps/ops/src/pages/EvaluationsPage.vue index 5ec098c..51a21e3 100644 --- a/apps/ops/src/pages/EvaluationsPage.vue +++ b/apps/ops/src/pages/EvaluationsPage.vue @@ -29,7 +29,7 @@
- {{ r.name || r.customerName || r.customer || r.email || 'Client' }} + {{ r.name || r.customerName || r.customer || r.email || 'Client' }} « {{ r.comment }} » Aucun commentaire diff --git a/apps/ops/src/pages/LegacySyncPage.vue b/apps/ops/src/pages/LegacySyncPage.vue index 0d60ac9..3fa91a9 100644 --- a/apps/ops/src/pages/LegacySyncPage.vue +++ b/apps/ops/src/pages/LegacySyncPage.vue @@ -126,7 +126,7 @@ + + +