Wizard: section Installation (palier financé) à l'étape Items

- Presets Standard 360→240 (10$/mois) / Simple 180→120 (5$/mois) / Aucune
- 2 cases éditables Prix original / Prix financé + aperçu live (barré, $/mois, crédité→0$)
- Alimente install_fee/install_regular du Service Contract → page d'acceptation affiche le détail
- Placé sous Code de référence dans la vue résidentielle

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
louispaulb 2026-06-03 19:20:04 -04:00
parent e2104c93f2
commit 37f4d5a941
2 changed files with 51 additions and 0 deletions

View File

@ -488,6 +488,42 @@
<q-icon name="error" size="13px" /> {{ referralError }} <q-icon name="error" size="13px" /> {{ referralError }}
</div> </div>
<!-- Installation financée créance réelle créditée chaque mois (conforme CRTC, pas de clawback) -->
<div class="referral-row q-mt-md" style="border-color:#bde5cb;background:#f4fff6">
<q-icon name="construction" size="18px" color="green-7" class="q-mr-sm" />
<div class="col" style="min-width:0">
<div class="referral-title" style="color:#00733a">Installation</div>
<div class="referral-desc">Financée sur {{ maxContractMonths }} mois, créditée chaque mois 0&nbsp;$ net pour le client tant qu'abonné</div>
</div>
<div class="row q-gutter-xs items-center no-wrap">
<q-btn v-for="p in INSTALL_PRESETS" :key="p.key" dense no-caps size="sm"
:outline="!isInstallPreset(p)" :unelevated="isInstallPreset(p)"
color="green-7" :text-color="isInstallPreset(p) ? 'white' : 'green-8'"
:label="p.label" @click="setInstallPreset(p)" />
</div>
</div>
<div v-if="installFee > 0 || installRegular > 0" class="row q-col-gutter-sm q-mt-xs items-center">
<div class="col-4">
<q-input v-model.number="installRegular" dense outlined type="number" step="0.01" min="0"
label="Prix original" :input-style="{ fontSize: '0.78rem' }"
title="Prix barré affiché au client (0 = aucun barré)">
<template v-slot:append><q-icon name="local_offer" size="13px" color="orange-7" /></template>
</q-input>
</div>
<div class="col-4">
<q-input v-model.number="installFee" dense outlined type="number" step="0.01" min="0"
label="Prix financé $" :input-style="{ fontSize: '0.78rem' }"
title="Montant réellement financé/dû (créance). 0 = installation gratuite" />
</div>
<div v-if="installFee > 0" class="col-4" style="font-size:0.76rem">
<div v-if="installRegular > installFee">
<s style="color:#9ca3af">{{ Number(installRegular).toFixed(2) }}$</s>
<b class="text-green-8 q-ml-xs">{{ Number(installFee).toFixed(2) }}$</b>
</div>
<div class="text-grey-7">+{{ (Number(installFee) / (maxContractMonths || 24)).toFixed(2) }}$/mois · crédité <b class="text-green-8">0$</b></div>
</div>
</div>
<!-- "Autre item" + "Item manuel" shortcuts live at the bottom <!-- "Autre item" + "Item manuel" shortcuts live at the bottom
the sticky cart pill at the top handles navigation to Sommaire the sticky cart pill at the top handles navigation to Sommaire
which is where cart details and totals are edited/reviewed. --> which is where cart details and totals are edited/reviewed. -->
@ -1697,6 +1733,7 @@ const { publish } = useWizardPublish({
acceptanceSentVia, publishedJobCount, acceptanceSentVia, publishedJobCount,
sendTo, sendChannel, sendTo, sendChannel,
publishedContractName, publishedContractName,
installFee, installRegular,
cancel () { cancel() }, cancel () { cancel() },
}, },
}) })
@ -1900,6 +1937,17 @@ const referralApplied = ref(false)
const referralChecking = ref(false) const referralChecking = ref(false)
const referralError = ref('') const referralError = ref('')
// Installation financée (CRTC-compliant) alimente install_fee / install_regular du contrat.
const installRegular = ref(0) // prix de référence barré (marketing)
const installFee = ref(0) // montant réellement financé/dû (créance)
const INSTALL_PRESETS = [
{ key: 'standard', label: 'Standard', regular: 360, fee: 240 },
{ key: 'simple', label: 'Simple', regular: 180, fee: 120 },
{ key: 'none', label: 'Aucune', regular: 0, fee: 0 },
]
function setInstallPreset (p) { installRegular.value = p.regular; installFee.value = p.fee }
function isInstallPreset (p) { return Number(installFee.value) === p.fee && Number(installRegular.value) === p.regular }
async function applyReferralCode () { async function applyReferralCode () {
const code = referralCode.value.trim().toUpperCase() const code = referralCode.value.trim().toUpperCase()
if (!code) return if (!code) return

View File

@ -13,6 +13,7 @@ export function useWizardPublish ({ props, emit, state }) {
acceptanceSentVia, publishedJobCount, acceptanceSentVia, publishedJobCount,
sendTo, sendChannel, sendTo, sendChannel,
publishedContractName, publishedContractName,
installFee, installRegular,
} = state } = state
async function resolveAddress () { async function resolveAddress () {
@ -265,6 +266,8 @@ export function useWizardPublish ({ props, emit, state }) {
duration_months: durationMonths, duration_months: durationMonths,
monthly_rate: monthlyRate, monthly_rate: monthlyRate,
monthly_regular: monthlyRegular > monthlyRate ? monthlyRegular : 0, monthly_regular: monthlyRegular > monthlyRate ? monthlyRegular : 0,
install_fee: installFee?.value || 0,
install_regular: (installRegular?.value || 0) > (installFee?.value || 0) ? installRegular.value : 0,
service_location: serviceLocation, service_location: serviceLocation,
quotation: orderDocName, quotation: orderDocName,
start_date: today, start_date: today,