Current state: custom CSS + vanilla Vue components Architecture: modular with composables, provide/inject pattern Ready for progressive migration to Quasar native components Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
103 lines
3.5 KiB
JavaScript
103 lines
3.5 KiB
JavaScript
// ── ERPNext Dispatch resource calls ─────────────────────────────────────────
|
|
// All ERPNext fetch() calls live here.
|
|
// Swap BASE_URL in config/erpnext.js to change the target server.
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
import { BASE_URL } from 'src/config/erpnext'
|
|
import { getCSRF } from './auth'
|
|
|
|
async function apiGet (path) {
|
|
const res = await fetch(BASE_URL + path, { credentials: 'include' })
|
|
const data = await res.json()
|
|
if (data.exc) throw new Error(data.exc)
|
|
return data
|
|
}
|
|
|
|
async function apiPut (doctype, name, body) {
|
|
const token = await getCSRF()
|
|
const res = await fetch(
|
|
`${BASE_URL}/api/resource/${encodeURIComponent(doctype)}/${encodeURIComponent(name)}`,
|
|
{
|
|
method: 'PUT',
|
|
credentials: 'include',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-Frappe-CSRF-Token': token,
|
|
},
|
|
body: JSON.stringify(body),
|
|
},
|
|
)
|
|
const data = await res.json()
|
|
if (data.exc) throw new Error(data.exc)
|
|
return data
|
|
}
|
|
|
|
export async function fetchTechnicians () {
|
|
const list = await apiGet('/api/resource/Dispatch%20Technician?fields=["name"]&limit=100')
|
|
const names = (list.data || []).map(t => t.name)
|
|
if (!names.length) return []
|
|
const docs = await Promise.all(
|
|
names.map(n => apiGet(`/api/resource/Dispatch%20Technician/${encodeURIComponent(n)}`).then(d => d.data))
|
|
)
|
|
return docs
|
|
}
|
|
|
|
// Fetch all jobs with child tables (assistants)
|
|
export async function fetchJobs (filters = null) {
|
|
// Step 1: get job names from list endpoint
|
|
let url = '/api/resource/Dispatch%20Job?fields=["name"]&limit=200'
|
|
if (filters) url += '&filters=' + encodeURIComponent(JSON.stringify(filters))
|
|
const list = await apiGet(url)
|
|
const names = (list.data || []).map(j => j.name)
|
|
if (!names.length) return []
|
|
// Step 2: fetch each doc individually (includes child tables)
|
|
const docs = await Promise.all(
|
|
names.map(n => apiGet(`/api/resource/Dispatch%20Job/${encodeURIComponent(n)}`).then(d => d.data))
|
|
)
|
|
return docs
|
|
}
|
|
|
|
export async function updateJob (name, payload) {
|
|
return apiPut('Dispatch Job', name, payload)
|
|
}
|
|
|
|
export async function createJob (payload) {
|
|
const token = await getCSRF()
|
|
const res = await fetch(
|
|
`${BASE_URL}/api/resource/Dispatch%20Job`,
|
|
{
|
|
method: 'POST',
|
|
credentials: 'include',
|
|
headers: { 'Content-Type': 'application/json', 'X-Frappe-CSRF-Token': token },
|
|
body: JSON.stringify(payload),
|
|
},
|
|
)
|
|
const data = await res.json()
|
|
if (data.exc) throw new Error(data.exc)
|
|
return data.data
|
|
}
|
|
|
|
export async function updateTech (name, payload) {
|
|
return apiPut('Dispatch Technician', name, payload)
|
|
}
|
|
|
|
export async function fetchTags () {
|
|
const data = await apiGet('/api/resource/Dispatch%20Tag?fields=["name","label","color","category"]&limit=200')
|
|
return data.data || []
|
|
}
|
|
|
|
export async function createTag (label, category = 'Custom', color = '#6b7280') {
|
|
const token = await getCSRF()
|
|
const res = await fetch(
|
|
`${BASE_URL}/api/resource/Dispatch%20Tag`,
|
|
{
|
|
method: 'POST',
|
|
credentials: 'include',
|
|
headers: { 'Content-Type': 'application/json', 'X-Frappe-CSRF-Token': token },
|
|
body: JSON.stringify({ label, category, color }),
|
|
},
|
|
)
|
|
const data = await res.json()
|
|
if (data.exc) throw new Error(data.exc)
|
|
return data.data
|
|
}
|