Compare commits
No commits in common. "main" and "v2.8.14" have entirely different histories.
@ -1,11 +0,0 @@
|
|||||||
# BMC Hub v2.8.15
|
|
||||||
|
|
||||||
## ALSO-perioder på ordrelinjer
|
|
||||||
|
|
||||||
- Viser perioden fra `Actual Charge Interval` på en ny linje under produktnavnet.
|
|
||||||
- Migration 248 retter eksisterende, ikke-eksporterede ALSO-ordrekladder.
|
|
||||||
- Allerede eksporterede ordre ændres ikke.
|
|
||||||
|
|
||||||
## Opgradering
|
|
||||||
|
|
||||||
Kør `migrations/248_also_order_period_line_break.sql` efter deployment.
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
# BMC Hub v2.8.16
|
|
||||||
|
|
||||||
## Flerlinjede ordrebeskrivelser
|
|
||||||
|
|
||||||
- Viser ordrelinjens produkt og ALSO-periode på separate linjer i både opret- og detaljevisningen.
|
|
||||||
- Bruger et kompakt tekstområde ved redigering, så linjeskiftet bevares ved gem og eksport til e-conomic.
|
|
||||||
- Viser linjeskift i forhåndsvisningen af en e-conomic-opdatering.
|
|
||||||
|
|
||||||
Der følger ingen ny databasemigration med denne release.
|
|
||||||
@ -2027,7 +2027,7 @@ class AlsoService:
|
|||||||
description = line.get("product_name") or line.get("matched_product_name") or "Cloud abonnement"
|
description = line.get("product_name") or line.get("matched_product_name") or "Cloud abonnement"
|
||||||
charge_period = _format_charge_period(line.get("charge_interval"))
|
charge_period = _format_charge_period(line.get("charge_interval"))
|
||||||
if charge_period:
|
if charge_period:
|
||||||
description = f"{description}\nPeriode: {charge_period}"
|
description = f"{description} · Periode: {charge_period}"
|
||||||
|
|
||||||
draft_lines.append(
|
draft_lines.append(
|
||||||
{
|
{
|
||||||
|
|||||||
@ -344,9 +344,9 @@
|
|||||||
const index = line.originalIndex;
|
const index = line.originalIndex;
|
||||||
const isManual = line.source_type === 'manual';
|
const isManual = line.source_type === 'manual';
|
||||||
const descriptionField = isManual
|
const descriptionField = isManual
|
||||||
? `<textarea class="form-control form-control-sm" rows="2" style="min-width:260px; resize:vertical;"
|
? `<input type="text" class="form-control form-control-sm" value="${escapeHtml(line.description || '')}"
|
||||||
onchange="ordreLines[${index}].description = this.value;">${escapeHtml(line.description || '')}</textarea>`
|
onchange="ordreLines[${index}].description = this.value;">`
|
||||||
: `<span style="white-space:pre-line">${escapeHtml(line.description || '-')}</span>`;
|
: escapeHtml(line.description || '-');
|
||||||
|
|
||||||
const manualActions = isManual
|
const manualActions = isManual
|
||||||
? `
|
? `
|
||||||
|
|||||||
@ -473,9 +473,9 @@
|
|||||||
const isExportedLine = line.export_status === 'exported';
|
const isExportedLine = line.export_status === 'exported';
|
||||||
const mayEditExportedDraft = isExportedLine && orderData && orderData.sync_status === 'exported' && !orderData.economic_invoice_number;
|
const mayEditExportedDraft = isExportedLine && orderData && orderData.sync_status === 'exported' && !orderData.economic_invoice_number;
|
||||||
const lockLine = isExportedLine && !mayEditExportedDraft;
|
const lockLine = isExportedLine && !mayEditExportedDraft;
|
||||||
const descriptionField = `<textarea class="form-control form-control-sm" rows="2" style="min-width:260px; resize:vertical;"
|
const descriptionField = `<input type="text" class="form-control form-control-sm" value="${escapeHtml(line.description || '')}"
|
||||||
${lockLine ? 'disabled' : ''}
|
${lockLine ? 'disabled' : ''}
|
||||||
onchange="orderLines[${index}].description = this.value;">${escapeHtml(line.description || '')}</textarea>`;
|
onchange="orderLines[${index}].description = this.value;">`;
|
||||||
|
|
||||||
const exportStatus = line.export_status || '-';
|
const exportStatus = line.export_status || '-';
|
||||||
const statusBadge = exportStatus === 'exported'
|
const statusBadge = exportStatus === 'exported'
|
||||||
@ -890,7 +890,7 @@
|
|||||||
const before = data.before || {}, after = data.after || {};
|
const before = data.before || {}, after = data.after || {};
|
||||||
const rows = (after.lines || []).map((line, i) => {
|
const rows = (after.lines || []).map((line, i) => {
|
||||||
const old = (before.lines || [])[i] || {};
|
const old = (before.lines || [])[i] || {};
|
||||||
return `<tr><td>${escapeHtml(line.product_number || '-')}</td><td style="white-space:pre-line">${escapeHtml(old.description)} → ${escapeHtml(line.description)}</td><td>${escapeHtml(old.quantity)} → ${escapeHtml(line.quantity)}</td><td>${escapeHtml(old.unit_price)} → ${escapeHtml(line.unit_price)}</td><td>${escapeHtml(old.discount_percentage)} → ${escapeHtml(line.discount_percentage)}</td></tr>`;
|
return `<tr><td>${escapeHtml(line.product_number || '-')}</td><td>${escapeHtml(old.description)} → ${escapeHtml(line.description)}</td><td>${escapeHtml(old.quantity)} → ${escapeHtml(line.quantity)}</td><td>${escapeHtml(old.unit_price)} → ${escapeHtml(line.unit_price)}</td><td>${escapeHtml(old.discount_percentage)} → ${escapeHtml(line.discount_percentage)}</td></tr>`;
|
||||||
}).join('');
|
}).join('');
|
||||||
document.getElementById('economicUpdateComparison').innerHTML = `<div class="mb-3"><strong>Noter:</strong> ${escapeHtml(before.notes || '-')} → ${escapeHtml(after.notes || '-')}</div><div class="table-responsive"><table class="table table-sm"><thead><tr><th>Vare</th><th>Beskrivelse</th><th>Antal</th><th>Pris</th><th>Rabat %</th></tr></thead><tbody>${rows}</tbody></table></div>`;
|
document.getElementById('economicUpdateComparison').innerHTML = `<div class="mb-3"><strong>Noter:</strong> ${escapeHtml(before.notes || '-')} → ${escapeHtml(after.notes || '-')}</div><div class="table-responsive"><table class="table table-sm"><thead><tr><th>Vare</th><th>Beskrivelse</th><th>Antal</th><th>Pris</th><th>Rabat %</th></tr></thead><tbody>${rows}</tbody></table></div>`;
|
||||||
bootstrap.Modal.getOrCreateInstance(document.getElementById('economicUpdatePreviewModal')).show();
|
bootstrap.Modal.getOrCreateInstance(document.getElementById('economicUpdatePreviewModal')).show();
|
||||||
|
|||||||
@ -1,26 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
|
|
||||||
-- Put the ALSO billing period on its own line for pending order drafts.
|
|
||||||
UPDATE ordre_drafts
|
|
||||||
SET lines_json = (
|
|
||||||
SELECT jsonb_agg(
|
|
||||||
CASE
|
|
||||||
WHEN item->>'source_type' = 'also_cloud'
|
|
||||||
AND COALESCE(item->>'description', '') LIKE '% · Periode: %'
|
|
||||||
THEN jsonb_set(
|
|
||||||
item,
|
|
||||||
'{description}',
|
|
||||||
to_jsonb(replace(item->>'description', ' · Periode: ', E'\nPeriode: '))
|
|
||||||
)
|
|
||||||
ELSE item
|
|
||||||
END
|
|
||||||
ORDER BY ordinality
|
|
||||||
)
|
|
||||||
FROM jsonb_array_elements(lines_json) WITH ORDINALITY AS source(item, ordinality)
|
|
||||||
),
|
|
||||||
updated_at = CURRENT_TIMESTAMP
|
|
||||||
WHERE sync_status = 'pending'
|
|
||||||
AND invoice_aggregate_key LIKE 'also-cloud-%'
|
|
||||||
AND lines_json::text LIKE '% · Periode: %';
|
|
||||||
|
|
||||||
COMMIT;
|
|
||||||
Loading…
Reference in New Issue
Block a user