/* eslint-disable handle-callback-err, no-return-assign, no-undef, no-useless-escape, */ // TODO: This file was created by bulk-decaffeinate. // Fix any style issues and re-enable lint. /* * decaffeinate suggestions: * DS101: Remove unnecessary use of Array.from * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ 'use strict' // # // Controller used in the admin invoices listing page // # Application.Controllers.controller('InvoicesController', ['$scope', '$state', 'Invoice', 'invoices', '$uibModal', 'growl', '$filter', 'Setting', 'settings', '_t', function ($scope, $state, Invoice, invoices, $uibModal, growl, $filter, Setting, settings, _t) { /* PRIVATE STATIC CONSTANTS */ // number of invoices loaded each time we click on 'load more...' const INVOICES_PER_PAGE = 20 /* PUBLIC SCOPE */ // # List of all users invoices $scope.invoices = invoices // Invoices filters $scope.searchInvoice = { date: null, name: '', reference: '' } // currently displayed page of invoices (search results) $scope.page = 1 // true when all invoices are loaded $scope.noMoreResults = false // # Default invoices ordering/sorting $scope.orderInvoice = '-reference' // # Invoices parameters $scope.invoice = { logo: null, reference: { model: '', help: null, templateUrl: 'editReference.html' }, code: { model: '', active: true, templateUrl: 'editCode.html' }, number: { model: '', help: null, templateUrl: 'editNumber.html' }, VAT: { rate: 19.6, active: false, templateUrl: 'editVAT.html' }, text: { content: '' }, legals: { content: '' } } // # Placeholding date for the invoice creation $scope.today = moment() // # Placeholding date for the reservation begin $scope.inOneWeek = moment().add(1, 'week').startOf('hour') // # Placeholding date for the reservation end $scope.inOneWeekAndOneHour = moment().add(1, 'week').add(1, 'hour').startOf('hour') // # // Change the invoices ordering criterion to the one provided // @param orderBy {string} ordering criterion // # $scope.setOrderInvoice = function (orderBy) { if ($scope.orderInvoice === orderBy) { $scope.orderInvoice = `-${orderBy}` } else { $scope.orderInvoice = orderBy } resetSearchInvoice() return invoiceSearch() } // # // Open a modal window asking the admin the details to refund the user about the provided invoice // @param invoice {Object} invoice inherited from angular's $resource // # $scope.generateAvoirForInvoice = function (invoice) { // open modal const modalInstance = $uibModal.open({ templateUrl: '<%= asset_path "admin/invoices/avoirModal.html" %>', controller: 'AvoirModalController', resolve: { invoice () { return invoice } } }) // once done, update the invoice model and inform the admin return modalInstance.result.then(function (res) { $scope.invoices.unshift(res.avoir) return Invoice.get({ id: invoice.id }, function (data) { invoice.has_avoir = data.has_avoir return growl.success(_t('refund_invoice_successfully_created')) }) }) } // # // Generate an invoice reference sample from the parametrized model // @returns {string} invoice reference sample // # $scope.mkReference = function () { let sample = $scope.invoice.reference.model if (sample) { // invoice number per day (dd..dd) sample = sample.replace(/d+(?![^\[]*])/g, (match, offset, string) => padWithZeros(2, match.length)) // invoice number per month (mm..mm) sample = sample.replace(/m+(?![^\[]*])/g, (match, offset, string) => padWithZeros(12, match.length)) // invoice number per year (yy..yy) sample = sample.replace(/y+(?![^\[]*])/g, (match, offset, string) => padWithZeros(8, match.length)) // date information sample = sample.replace(/[YMD]+(?![^\[]*])/g, (match, offset, string) => $scope.today.format(match)) // information about online selling (X[text]) sample = sample.replace(/X\[([^\]]+)\]/g, (match, p1, offset, string) => p1) // information about wallet (W[text]) - does not apply here sample = sample.replace(/W\[([^\]]+)\]/g, '') // information about refunds (R[text]) - does not apply here sample = sample.replace(/R\[([^\]]+)\]/g, '') } return sample } // # // Generate an order nmuber sample from the parametrized model // @returns {string} invoice reference sample // # $scope.mkNumber = function () { let sample = $scope.invoice.number.model if (sample) { // global order number (nn..nn) sample = sample.replace(/n+(?![^\[]*])/g, (match, offset, string) => padWithZeros(327, match.length)) // order number per year (yy..yy) sample = sample.replace(/y+(?![^\[]*])/g, (match, offset, string) => padWithZeros(8, match.length)) // order number per month (mm..mm) sample = sample.replace(/m+(?![^\[]*])/g, (match, offset, string) => padWithZeros(12, match.length)) // order number per day (dd..dd) sample = sample.replace(/d+(?![^\[]*])/g, (match, offset, string) => padWithZeros(2, match.length)) // date information sample = sample.replace(/[YMD]+(?![^\[]*])/g, (match, offset, string) => $scope.today.format(match)) } return sample } // # // Open a modal dialog allowing the user to edit the invoice reference generation template // # $scope.openEditReference = function () { const modalInstance = $uibModal.open({ animation: true, templateUrl: $scope.invoice.reference.templateUrl, size: 'lg', resolve: { model () { return $scope.invoice.reference.model } }, controller ($scope, $uibModalInstance, model) { $scope.model = model $scope.ok = () => $uibModalInstance.close($scope.model) return $scope.cancel = () => $uibModalInstance.dismiss('cancel') } }) return modalInstance.result.then(model => Setting.update({ name: 'invoice_reference' }, { value: model }, function (data) { $scope.invoice.reference.model = model return growl.success(_t('invoice_reference_successfully_saved')) } , function (error) { growl.error(_t('an_error_occurred_while_saving_invoice_reference')) return console.error(error) }) ) } // # // Open a modal dialog allowing the user to edit the invoice code // # $scope.openEditCode = function () { const modalInstance = $uibModal.open({ animation: true, templateUrl: $scope.invoice.code.templateUrl, size: 'lg', resolve: { model () { return $scope.invoice.code.model }, active () { return $scope.invoice.code.active } }, controller ($scope, $uibModalInstance, model, active) { $scope.codeModel = model $scope.isSelected = active $scope.ok = () => $uibModalInstance.close({ model: $scope.codeModel, active: $scope.isSelected }) return $scope.cancel = () => $uibModalInstance.dismiss('cancel') } }) return modalInstance.result.then(function (result) { Setting.update({ name: 'invoice_code-value' }, { value: result.model }, function (data) { $scope.invoice.code.model = result.model if (result.active) { return growl.success(_t('invoicing_code_succesfully_saved')) } } , function (error) { growl.error(_t('an_error_occurred_while_saving_the_invoicing_code')) return console.error(error) }) return Setting.update({ name: 'invoice_code-active' }, { value: result.active ? 'true' : 'false' }, function (data) { $scope.invoice.code.active = result.active if (result.active) { return growl.success(_t('code_successfully_activated')) } else { return growl.success(_t('code_successfully_disabled')) } } , function (error) { growl.error(_t('an_error_occurred_while_activating_the_invoicing_code')) return console.error(error) }) }) } // # // Open a modal dialog allowing the user to edit the invoice number // # $scope.openEditInvoiceNb = function () { const modalInstance = $uibModal.open({ animation: true, templateUrl: $scope.invoice.number.templateUrl, size: 'lg', resolve: { model () { return $scope.invoice.number.model } }, controller ($scope, $uibModalInstance, model) { $scope.model = model $scope.ok = () => $uibModalInstance.close($scope.model) return $scope.cancel = () => $uibModalInstance.dismiss('cancel') } }) return modalInstance.result.then(model => Setting.update({ name: 'invoice_order-nb' }, { value: model }, function (data) { $scope.invoice.number.model = model return growl.success(_t('order_number_successfully_saved')) } , function (error) { growl.error(_t('an_error_occurred_while_saving_the_order_number')) return console.error(error) }) ) } // # // Open a modal dialog allowing the user to edit the VAT parameters for the invoices // The VAT can be disabled and its rate can be configured // # $scope.openEditVAT = function () { const modalInstance = $uibModal.open({ animation: true, templateUrl: $scope.invoice.VAT.templateUrl, size: 'lg', resolve: { rate () { return $scope.invoice.VAT.rate }, active () { return $scope.invoice.VAT.active } }, controller ($scope, $uibModalInstance, rate, active) { $scope.rate = rate $scope.isSelected = active $scope.ok = () => $uibModalInstance.close({ rate: $scope.rate, active: $scope.isSelected }) return $scope.cancel = () => $uibModalInstance.dismiss('cancel') } }) return modalInstance.result.then(function (result) { Setting.update({ name: 'invoice_VAT-rate' }, { value: result.rate + '' }, function (data) { $scope.invoice.VAT.rate = result.rate if (result.active) { return growl.success(_t('VAT_rate_successfully_saved')) } } , function (error) { growl.error(_t('an_error_occurred_while_saving_the_VAT_rate')) return console.error(error) }) return Setting.update({ name: 'invoice_VAT-active' }, { value: result.active ? 'true' : 'false' }, function (data) { $scope.invoice.VAT.active = result.active if (result.active) { return growl.success(_t('VAT_successfully_activated')) } else { return growl.success(_t('VAT_successfully_disabled')) } } , function (error) { growl.error(_t('an_error_occurred_while_activating_the_VAT')) return console.error(error) }) }) } // # // Callback to save the value of the text zone when editing is done // # $scope.textEditEnd = function (event) { const parsed = parseHtml($scope.invoice.text.content) return Setting.update({ name: 'invoice_text' }, { value: parsed }, function (data) { $scope.invoice.text.content = parsed return growl.success(_t('text_successfully_saved')) } , function (error) { growl.error(_t('an_error_occurred_while_saving_the_text')) return console.error(error) }) } // # // Callback to save the value of the legal information zone when editing is done // # $scope.legalsEditEnd = function (event) { const parsed = parseHtml($scope.invoice.legals.content) return Setting.update({ name: 'invoice_legals' }, { value: parsed }, function (data) { $scope.invoice.legals.content = parsed return growl.success(_t('address_and_legal_information_successfully_saved')) } , function (error) { growl.error(_t('an_error_occurred_while_saving_the_address_and_the_legal_information')) return console.error(error) }) } // # // Callback when any of the filters changes. // Full reload the results list // # $scope.handleFilterChange = function () { resetSearchInvoice() return invoiceSearch() } // # // Callback for the 'load more' button. // Will load the next results of the current search, if any // # $scope.showNextInvoices = function () { $scope.page += 1 return invoiceSearch(true) } /* PRIVATE SCOPE */ // # // Kind of constructor: these actions will be realized first when the controller is loaded // # const initialize = function () { if (!invoices[0] || (invoices[0].maxInvoices <= $scope.invoices.length)) { $scope.noMoreResults = true } // retrieve settings from the DB through the API $scope.invoice.legals.content = settings['invoice_legals'] $scope.invoice.text.content = settings['invoice_text'] $scope.invoice.VAT.rate = parseFloat(settings['invoice_VAT-rate']) $scope.invoice.VAT.active = (settings['invoice_VAT-active'] === 'true') $scope.invoice.number.model = settings['invoice_order-nb'] $scope.invoice.code.model = settings['invoice_code-value'] $scope.invoice.code.active = (settings['invoice_code-active'] === 'true') $scope.invoice.reference.model = settings['invoice_reference'] $scope.invoice.logo = { filetype: 'image/png', filename: 'logo.png', base64: settings['invoice_logo'] } // Watch the logo, when a change occurs, save it return $scope.$watch('invoice.logo', function () { if ($scope.invoice.logo && $scope.invoice.logo.filesize) { return Setting.update({ name: 'invoice_logo' }, { value: $scope.invoice.logo.base64 }, data => growl.success(_t('logo_successfully_saved')) , function (error) { growl.error(_t('an_error_occurred_while_saving_the_logo')) return console.error(error) }) } }) } // # // Output the given integer with leading zeros. If the given value is longer than the given // length, it will be truncated. // @param value {number} the integer to pad // @param length {number} the length of the resulting string. // # var padWithZeros = (value, length) => (1e15 + value + '').slice(-length) // # // Remove every unsupported html tag from the given html text (like

, , ...). // The supported tags are , , and
. // @param html {string} single line html text // @return {string} multi line simplified html text // # var parseHtml = html => html = html.replace(/<\/?(\w+)((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)\/?>/g, function (match, p1, offset, string) { if (['b', 'u', 'i', 'br'].includes(p1)) { return match } else { return '' } }) // # // Reinitialize the context of invoices' search to display new results set // # var resetSearchInvoice = function () { $scope.page = 1 return $scope.noMoreResults = false } // # // Run a search query with the current parameters set concerning invoices, then affect or concat the results // to $scope.invoices // @param concat {boolean} if true, the result will be append to $scope.invoices instead of being affected // # var invoiceSearch = concat => Invoice.list({ query: { number: $scope.searchInvoice.reference, customer: $scope.searchInvoice.name, date: $scope.searchInvoice.date, order_by: $scope.orderInvoice, page: $scope.page, size: INVOICES_PER_PAGE } }, function (invoices) { if (concat) { $scope.invoices = $scope.invoices.concat(invoices) } else { $scope.invoices = invoices } if (!invoices[0] || (invoices[0].maxInvoices <= $scope.invoices.length)) { return $scope.noMoreResults = true } }) // # !!! MUST BE CALLED AT THE END of the controller return initialize() } ]) // # // Controller used in the invoice refunding modal window // # Application.Controllers.controller('AvoirModalController', ['$scope', '$uibModalInstance', 'invoice', 'Invoice', 'growl', '_t', function ($scope, $uibModalInstance, invoice, Invoice, growl, _t) { /* PUBLIC SCOPE */ // # invoice linked to the current refund $scope.invoice = invoice // # Associative array containing invoice_item ids associated with boolean values $scope.partial = {} // # Default refund parameters $scope.avoir = { invoice_id: invoice.id, subscription_to_expire: false, invoice_items_ids: [] } // # Possible refunding methods $scope.avoirModes = [ { name: _t('none'), value: 'none' }, { name: _t('by_cash'), value: 'cash' }, { name: _t('by_cheque'), value: 'cheque' }, { name: _t('by_transfer'), value: 'transfer' }, { name: _t('by_wallet'), value: 'wallet' } ] // # If a subscription was took with the current invoice, should it be canceled or not $scope.subscriptionExpireOptions = {} $scope.subscriptionExpireOptions[_t('yes')] = true $scope.subscriptionExpireOptions[_t('no')] = false // # AngularUI-Bootstrap datepicker parameters to define when to refund $scope.datePicker = { format: Fablab.uibDateFormat, opened: false, // default: datePicker is not shown options: { startingDay: Fablab.weekStartingDay } } // # // Callback to open the datepicker // # $scope.openDatePicker = function ($event) { $event.preventDefault() $event.stopPropagation() return $scope.datePicker.opened = true } // # // Validate the refunding and generate a refund invoice // # $scope.ok = function () { // check that at least 1 element of the invoice is refunded $scope.avoir.invoice_items_ids = [] for (let itemId in $scope.partial) { const refundItem = $scope.partial[itemId] if (refundItem) { $scope.avoir.invoice_items_ids.push(parseInt(itemId)) } } if ($scope.avoir.invoice_items_ids.length === 0) { return growl.error(_t('you_must_select_at_least_one_element_to_create_a_refund')) } else { return Invoice.save({ avoir: $scope.avoir }, avoir => // success $uibModalInstance.close({ avoir, invoice: $scope.invoice }) , err => // failed growl.error(_t('unable_to_create_the_refund')) ) } } // # // Cancel the refund, dismiss the modal window // # $scope.cancel = () => $uibModalInstance.dismiss('cancel') /* PRIVATE SCOPE */ // # // Kind of constructor: these actions will be realized first when the controller is loaded // # const initialize = function () { // # if the invoice was payed with stripe, allow to refund through stripe Invoice.get({ id: invoice.id }, function (data) { $scope.invoice = data // default : all elements of the invoice are refund return Array.from(data.items).map((item) => ($scope.partial[item.id] = (typeof item.avoir_item_id !== 'number'))) }) if (invoice.stripe) { return $scope.avoirModes.push({ name: _t('online_payment'), value: 'stripe' }) } } // # !!! MUST BE CALLED AT THE END of the controller return initialize() } ])