Integrates the Dispatch PWA (Vue/Quasar) into the gigafibre-fsm monorepo. Full git history accessible via `git log -- apps/dispatch/`. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
98 lines
5.1 KiB
JavaScript
98 lines
5.1 KiB
JavaScript
// ── Dispatch Settings — lecture/écriture du DocType Single ERPNext ───────────
|
|
import { BASE_URL } from 'src/config/erpnext'
|
|
import { getCSRF } from './auth'
|
|
|
|
const DOCTYPE = 'Dispatch Settings'
|
|
const NAME = 'Dispatch Settings'
|
|
|
|
function isDocTypeError (body) {
|
|
const s = JSON.stringify(body)
|
|
return s.includes('dispatch_settings') || s.includes('DoesNotExist') || s.includes('No module named')
|
|
}
|
|
|
|
export async function fetchSettings () {
|
|
const r = await fetch(`${BASE_URL}/api/resource/${DOCTYPE}/${NAME}`, {
|
|
credentials: 'include',
|
|
})
|
|
if (!r.ok) {
|
|
const body = await r.json().catch(() => ({}))
|
|
if (r.status === 404 || isDocTypeError(body)) throw new Error('DOCTYPE_NOT_FOUND')
|
|
throw new Error(`Erreur HTTP ${r.status}`)
|
|
}
|
|
const body = await r.json()
|
|
// Frappe peut retourner 200 avec une exception dans le corps
|
|
if (body.exc_type || body.exception) {
|
|
if (isDocTypeError(body)) throw new Error('DOCTYPE_NOT_FOUND')
|
|
throw new Error(body.exc_type || 'Erreur Frappe')
|
|
}
|
|
return body.data
|
|
}
|
|
|
|
export async function saveSettings (payload) {
|
|
const csrf = await getCSRF()
|
|
const r = await fetch(`${BASE_URL}/api/resource/${DOCTYPE}/${NAME}`, {
|
|
method: 'PUT',
|
|
credentials: 'include',
|
|
headers: { 'Content-Type': 'application/json', 'X-Frappe-CSRF-Token': csrf || '' },
|
|
body: JSON.stringify(payload),
|
|
})
|
|
const body = await r.json().catch(() => ({}))
|
|
if (!r.ok || body.exc_type || body.exception) {
|
|
if (isDocTypeError(body)) throw new Error('DOCTYPE_NOT_FOUND')
|
|
throw new Error(body._error_message || body.exc_type || `Erreur HTTP ${r.status}`)
|
|
}
|
|
return body
|
|
}
|
|
|
|
// ── Création du DocType via API (bouton Initialiser dans l'admin) ─────────────
|
|
const DOCTYPE_FIELDS = [
|
|
{ fieldname: 'erp_section', fieldtype: 'Section Break', label: 'ERPNext / Frappe' },
|
|
{ fieldname: 'erp_url', fieldtype: 'Data', label: 'URL du serveur', default: 'http://localhost:8080' },
|
|
{ fieldname: 'erp_api_key', fieldtype: 'Data', label: 'API Key' },
|
|
{ fieldname: 'erp_api_secret', fieldtype: 'Password', label: 'API Secret' },
|
|
{ fieldname: 'mapbox_section', fieldtype: 'Section Break', label: 'Mapbox' },
|
|
{ fieldname: 'mapbox_token', fieldtype: 'Data', label: 'Token public (pk_)' },
|
|
{ fieldname: 'twilio_section', fieldtype: 'Section Break', label: 'Twilio — SMS' },
|
|
{ fieldname: 'twilio_account_sid', fieldtype: 'Data', label: 'Account SID' },
|
|
{ fieldname: 'twilio_auth_token', fieldtype: 'Password', label: 'Auth Token' },
|
|
{ fieldname: 'twilio_from_number', fieldtype: 'Data', label: 'Numéro expéditeur' },
|
|
{ fieldname: 'stripe_section', fieldtype: 'Section Break', label: 'Stripe — Paiements' },
|
|
{ fieldname: 'stripe_mode', fieldtype: 'Select', label: 'Mode', options: 'test\nlive', default: 'test' },
|
|
{ fieldname: 'stripe_publishable_key', fieldtype: 'Data', label: 'Clé publique (pk_)' },
|
|
{ fieldname: 'stripe_secret_key', fieldtype: 'Password', label: 'Clé secrète (sk_)' },
|
|
{ fieldname: 'stripe_webhook_secret',fieldtype: 'Password', label: 'Webhook Secret (whsec_)' },
|
|
{ fieldname: 'n8n_section', fieldtype: 'Section Break', label: 'n8n — Automatisation' },
|
|
{ fieldname: 'n8n_url', fieldtype: 'Data', label: 'URL n8n', default: 'http://localhost:5678' },
|
|
{ fieldname: 'n8n_api_key', fieldtype: 'Password', label: 'API Key n8n' },
|
|
{ fieldname: 'n8n_webhook_base', fieldtype: 'Data', label: 'Base URL webhooks', default: 'http://localhost:5678/webhook' },
|
|
{ fieldname: 'sms_section', fieldtype: 'Section Break', label: 'Templates SMS' },
|
|
{ fieldname: 'sms_enroute', fieldtype: 'Text', label: 'Technicien en route',
|
|
default: 'Bonjour {client_name}, votre technicien {tech_name} est en route et arrivera dans environ {eta} minutes. Réf: {job_id}' },
|
|
{ fieldname: 'sms_completed', fieldtype: 'Text', label: 'Service complété',
|
|
default: 'Bonjour {client_name}, votre service ({job_id}) a été complété avec succès. Merci de votre confiance !' },
|
|
{ fieldname: 'sms_assigned', fieldtype: 'Text', label: 'Job assigné (technicien)',
|
|
default: 'Nouveau job assigné : {job_id} — {client_name}, {address}. Durée estimée : {duration}h.' },
|
|
]
|
|
|
|
export async function createDocType () {
|
|
const csrf = await getCSRF()
|
|
const r = await fetch(`${BASE_URL}/api/resource/DocType`, {
|
|
method: 'POST',
|
|
credentials: 'include',
|
|
headers: { 'Content-Type': 'application/json', 'X-Frappe-CSRF-Token': csrf || '' },
|
|
body: JSON.stringify({
|
|
name: DOCTYPE, module: 'Core', custom: 1, is_single: 1, track_changes: 0,
|
|
fields: DOCTYPE_FIELDS,
|
|
permissions: [
|
|
{ role: 'System Manager', read: 1, write: 1, create: 1 },
|
|
{ role: 'Administrator', read: 1, write: 1, create: 1 },
|
|
],
|
|
}),
|
|
})
|
|
const body = await r.json().catch(() => ({}))
|
|
if (!r.ok || body.exc_type) {
|
|
throw new Error(body._error_message || body.exc_type || `Erreur HTTP ${r.status}`)
|
|
}
|
|
return body
|
|
}
|