Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee274a939a | ||
|
|
b95815de0d |
11
MDfile/RELEASE_NOTES_v2.8.15.md
Normal file
11
MDfile/RELEASE_NOTES_v2.8.15.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# 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.
|
||||||
9
MDfile/RELEASE_NOTES_v2.8.16.md
Normal file
9
MDfile/RELEASE_NOTES_v2.8.16.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# 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} · Periode: {charge_period}"
|
description = f"{description}\nPeriode: {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
|
||||||
? `<input type="text" class="form-control form-control-sm" value="${escapeHtml(line.description || '')}"
|
? `<textarea class="form-control form-control-sm" rows="2" style="min-width:260px; resize:vertical;"
|
||||||
onchange="ordreLines[${index}].description = this.value;">`
|
onchange="ordreLines[${index}].description = this.value;">${escapeHtml(line.description || '')}</textarea>`
|
||||||
: escapeHtml(line.description || '-');
|
: `<span style="white-space:pre-line">${escapeHtml(line.description || '-')}</span>`;
|
||||||
|
|
||||||
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 = `<input type="text" class="form-control form-control-sm" value="${escapeHtml(line.description || '')}"
|
const descriptionField = `<textarea class="form-control form-control-sm" rows="2" style="min-width:260px; resize:vertical;"
|
||||||
${lockLine ? 'disabled' : ''}
|
${lockLine ? 'disabled' : ''}
|
||||||
onchange="orderLines[${index}].description = this.value;">`;
|
onchange="orderLines[${index}].description = this.value;">${escapeHtml(line.description || '')}</textarea>`;
|
||||||
|
|
||||||
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>${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 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>`;
|
||||||
}).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();
|
||||||
|
|||||||
26
migrations/248_also_order_period_line_break.sql
Normal file
26
migrations/248_also_order_period_line_break.sql
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
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