UNPKG

ace-customizer-lib

Version:

ACE knowledge platform scripts

345 lines (282 loc) 10 kB
/** * Credit note calculator * Framework: Vue.js * Author: Jannik Maag, Telia Company * Date: 13 July 2022 * License: MIT */ Vue.component("creditnote-calculator", { data: function () { return { dateRangeCollection: [], startDate: null, endDate: null, calculationRows: [], creditnoteDateInputValue: null, BRAND_ENUMS: { CALLME: "CALLME", TELIA: "TELIA", MITTELE: "MITTELE", }, }; }, created() { this.creditnoteDateInputValue = new Date(); }, mounted() { const amountOfRows = 7; for (let i = 0; i < amountOfRows; i++) { this.calculationRows.push({ id: `calculation-${i}`, inputPrice: null, resultPrice: null, note: "", }); } }, computed: { totalWithVat() { let sum = 0; this.calculationRows.forEach((x) => (sum += parseFloat(x.resultPrice))); return sum.toFixed(2); }, totalWithoutVat() { // Danish VAT is 25% return parseFloat(this.totalWithVat / 1.25).toFixed(2); }, creditnoteDate() { return new Date(this.creditnoteDateInputValue); }, visibleOnBillDate() { const date = this.creditnoteDate; const toString = this.convertDateToString; const teliaBillingDate = this.dateIsBeforeDayInMonth(date, 5) ? this.addMonthsToDate(date, 1) : this.addMonthsToDate(date, 2); const callmeAndMitteleBillingDate = this.dateIsBeforeDayInMonth(date, 15) ? this.addMonthsToDate(date, 1) : this.addMonthsToDate(date, 2); return { [this.BRAND_ENUMS.TELIA]: toString(teliaBillingDate), [this.BRAND_ENUMS.CALLME]: toString(callmeAndMitteleBillingDate), [this.BRAND_ENUMS.MITTELE]: toString(callmeAndMitteleBillingDate), }; }, dateRangeIsValid() { return !!this.startDate && !!this.endDate; }, }, watch: { startDate() { this.dateChangeHandler(); }, endDate() { this.dateChangeHandler(); }, calculationRows: { deep: true, handler() { this.reCalculateAllRows(); }, }, creditnoteDateInputValue(date) { if (!date) { // Default to todays date if user clears the date input field. this.creditnoteDateInputValue = new Date(); } }, }, methods: { calculateResultPrice(monthlyPrice) { if (!monthlyPrice || !this.dateRangeIsValid) return 0; const converted = parseFloat(monthlyPrice.replace(",", ".")); console.log(converted); let result = 0; this.dateRangeCollection.forEach((x) => { // No reason to calculate if there's no days in the month if (x.days <= 0) return; // Find out how many calendar days there is in the given month const daysInMonth = new Date(x.year, x.month + 1, 0).getDate(); /* * BUSINESS RULES FOR CREDIT NOTE CALCULATION * In a month of 28 days, 28 days should be considered a full month * In a month of 30 days, 30 days should be considered a full month * In a month of 31 days, both 30 days and 31 days should be considered a full month */ const shouldBeConsideredFullMonth = x.days >= daysInMonth; if (shouldBeConsideredFullMonth) { // Add the subscriptions full price for a single month result += parseFloat(monthlyPrice); } else { // Calculate price based on subscription daily price // Must always be based on 30 days as per business rule result += (parseFloat(monthlyPrice) / 30) * x.days; } }); return parseFloat(result); }, parseComma(value) { return value ? value.replace(",", ".") : value; }, reCalculateAllRows() { this.calculationRows.forEach((x) => { x.resultPrice = this.calculateResultPrice( this.parseComma(x.inputPrice) ); }); }, convertDateToString(date) { return `${date.getDate()}/${date.getMonth() + 1}/${date.getFullYear()}`; }, dateChangeHandler() { this.clearDateRangeCollection(); if (this.dateRangeIsValid) { this.createDateRangeCollection(); this.reCalculateAllRows(); } }, createDateRangeCollection() { if (!this.dateRangeIsValid) return; let loop = new Date(this.startDate); const end = new Date(this.endDate); while (loop <= end) { const entry = this.dateRangeCollection.find((entry) => { // Find an entry that matches the year and month return ( entry.year === loop.getFullYear() && entry.month === loop.getMonth() ); }); if (entry) entry.days++; else this.dateRangeCollection.push({ month: loop.getMonth(), year: loop.getFullYear(), days: 1, // TODO: should start from 1 perhaps somewhere else }); const newDate = loop.setDate(loop.getDate() + 1); loop = new Date(newDate); } }, clearDateRangeCollection() { this.dateRangeCollection = []; }, clearEverything() { this.startDate = null; this.endDate = null; this.creditnoteDateInputValue = new Date(); this.clearDateRangeCollection(); this.calculationRows.forEach((row) => { row.inputPrice = null; row.resultPrice = null; row.note = ""; }); }, copyToClipboard(value) { navigator.clipboard .writeText(value) .then(() => { alert("Kopieret ".concat(value)); }) .catch(() => { alert( "Der skete en FEJL. Din browser er muligvis ikke understøttet." ); }); }, addMonthsToDate(date, monthsToAdd) { if (!date || !monthsToAdd) return date; return new Date(date.getFullYear(), date.getMonth() + monthsToAdd, 1); }, dateIsBeforeDayInMonth(date, day) { if (!date || !day) return false; const dateToCompare = new Date(date.getFullYear(), date.getMonth(), day); return date <= dateToCompare; }, }, template: ` <div class="creditNoteCalc_wrapper" id="creditnotecalc3"> <div class="prop-container"> <div class="upper"> <div class="date-pick-block date-pick-block date-picker-x-container"> <div class="btn-keep"> <i class="fa fa-calendar" style="color: #393939; font-size: 22px;"></i> </div> <p class="date-pick-text">Periode:</p> <input type="date" class="date-period" v-model="startDate" /> <p class="date-pick-text">til</p> <input type="date" class="date-period" v-model="endDate" /> <button v-on:click="clearEverything()" class="calc-reset-btn"> Nulstil alt </button> </div> </div> <div class="main-area"> <section class="calc-row"> <div class="row-part note">Abonnementspris</div> <div class="row-part note">Kreditnota</div> <div class="row-part note">Notat</div> </section> <section v-for="row in calculationRows" key="row.id" class="calc-row"> <div class="row-part"> <input class="priceInput" placeholder="Tast pris" type="text" v-model="row.inputPrice" /> </div> <div class="row-part"> <div class="calcResult">{{ !dateRangeIsValid ? "Vælg en periode" : row.resultPrice }}</div> </div> <div class="row-part"> <input class="noteInput" placeholder="Skriv notat" v-model="row.note" /> </div> </section> </div> </div> <div class="result-container"> <section class="upper"> <div class="result-header"> <div class="header-icon"> <i class="fa fa-money" style="color: #f4f4f4; font-size: 32px;"></i> </div> <h2 class="result-headline">Kreditnota</h2> </div> </section> <section class="middle"> <div class="total-container"> <p class="total-text">Total kreditnota inkl. moms</p> <div id="creditTotal">{{ totalWithVat }} kr.</div> <div id="icon-copy-total-credit" v-on:click="copyToClipboard(totalWithVat)"> <i class="fa fa-copy" style="color: white; cursor: pointer; font-size: 20px;"></i> </div> <p class="total-text">Eksl. moms</p> <div id="exMoms">{{ totalWithoutVat }} kr.</div> <div id="icon-copy-ex-moms" v-on:click="copyToClipboard(totalWithoutVat)"> <i class="fa fa-copy" style="color: white; cursor: pointer; font-size: 20px;"></i> </div> </div> <div class="littleTip"> <div style="font-size: 12px;"> <p>Hvis kreditnotaen lægges på d. {{ convertDateToString(creditnoteDate) }} <br /> Vil den fremgå af kundens regning d.:</p> <p> <div>Telia: {{ visibleOnBillDate[BRAND_ENUMS.TELIA] }}</div> <div>Callme: {{ visibleOnBillDate[BRAND_ENUMS.CALLME] }}</div> <div>Mit Tele: {{ visibleOnBillDate[BRAND_ENUMS.MITTELE] }}</div> </p> <p style="margin: unset;">Vælg en anden dato:</p> <input style="line-height: unset; color: black;" type="date" v-model="creditnoteDateInputValue" /> </div> </div> </section> </div> </div> `, });