mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-19 08:52:25 +01:00
Merge branch 'dev' for release 5.6.1
This commit is contained in:
commit
5ab2989a61
12
CHANGELOG.md
12
CHANGELOG.md
@ -1,5 +1,17 @@
|
|||||||
# Changelog Fab-manager
|
# Changelog Fab-manager
|
||||||
|
|
||||||
|
## v5.6.1 2023 January 6
|
||||||
|
|
||||||
|
- Fix a bug: allow decimal values for VAT rates
|
||||||
|
- Fix a bug: canceled reservations/slots not shown as it in the reservations dashboard
|
||||||
|
- Fix a bug: no main item on some invoices
|
||||||
|
- Fix a bug: unable to build accounting lines if no invoices
|
||||||
|
- Fix a bug: unable to apply rounding correction on accounting lines
|
||||||
|
- Fix a bug: empty object for some invoice item
|
||||||
|
- Fix a bug: unable to filter Show only slots with reservations in public calendar for admin
|
||||||
|
- Fix a security issue: updated json5 to 1.0.2 to fix [CVE-2022-46175](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2022-46175)
|
||||||
|
- [TODO DEPLOY] `rails fablab:fix_invoice_items` => run this script BEFORE running the migrations
|
||||||
|
|
||||||
## v5.6.0 2023 January 5
|
## v5.6.0 2023 January 5
|
||||||
|
|
||||||
- Ability to group machines by categories
|
- Ability to group machines by categories
|
||||||
|
@ -89,18 +89,25 @@ const ReservationsPanel: React.FC<SpaceReservationsProps> = ({ userId, onError,
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if all slots of the given reservation are canceled
|
||||||
|
*/
|
||||||
|
const isCancelled = (reservation: Reservation): boolean => {
|
||||||
|
return reservation.slots_reservations_attributes.map(sr => sr.canceled_at).every(ca => ca != null);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the reservation in a user-friendly way
|
* Render the reservation in a user-friendly way
|
||||||
*/
|
*/
|
||||||
const renderReservation = (reservation: Reservation, state: 'past' | 'futur'): ReactNode => {
|
const renderReservation = (reservation: Reservation, state: 'past' | 'futur'): ReactNode => {
|
||||||
return (
|
return (
|
||||||
<li key={reservation.id} className="reservation">
|
<li key={reservation.id} className="reservation">
|
||||||
<a className={`reservation-title ${details[reservation.id] ? 'clicked' : ''}`} onClick={toggleDetails(reservation.id)}>
|
<a className={`reservation-title ${details[reservation.id] ? 'clicked' : ''} ${isCancelled(reservation) ? 'canceled' : ''}`} onClick={toggleDetails(reservation.id)}>
|
||||||
{reservation.reservable.name} - {FormatLib.date(reservation.slots_reservations_attributes[0].slot_attributes.start_at)}
|
{reservation.reservable.name} - {FormatLib.date(reservation.slots_reservations_attributes[0].slot_attributes.start_at)}
|
||||||
</a>
|
</a>
|
||||||
{details[reservation.id] && <FabPopover title={t('app.logged.dashboard.reservations.reservations_panel.slots_details')}>
|
{details[reservation.id] && <FabPopover title={t('app.logged.dashboard.reservations.reservations_panel.slots_details')}>
|
||||||
{reservation.slots_reservations_attributes.filter(s => filterSlot(s, state)).map(
|
{reservation.slots_reservations_attributes.filter(s => filterSlot(s, state)).map(
|
||||||
slotReservation => <span key={slotReservation.id} className="slot-details">
|
slotReservation => <span key={slotReservation.id} className={`slot-details ${slotReservation.canceled_at ? 'canceled' : ''}`}>
|
||||||
{FormatLib.date(slotReservation.slot_attributes.start_at)}, {FormatLib.time(slotReservation.slot_attributes.start_at)} - {FormatLib.time(slotReservation.slot_attributes.end_at)}
|
{FormatLib.date(slotReservation.slot_attributes.start_at)}, {FormatLib.time(slotReservation.slot_attributes.start_at)} - {FormatLib.time(slotReservation.slot_attributes.end_at)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
@ -115,6 +115,7 @@ export const VatSettingsModal: React.FC<VatSettingsModalProps> = ({ isOpen, togg
|
|||||||
rules={{ required: true }}
|
rules={{ required: true }}
|
||||||
tooltip={t('app.admin.vat_settings_modal.VAT_rate_help')}
|
tooltip={t('app.admin.vat_settings_modal.VAT_rate_help')}
|
||||||
type='number'
|
type='number'
|
||||||
|
step={0.001}
|
||||||
label={t('app.admin.vat_settings_modal.VAT_rate')}
|
label={t('app.admin.vat_settings_modal.VAT_rate')}
|
||||||
addOn={<ClockCounterClockwise size={24}/>}
|
addOn={<ClockCounterClockwise size={24}/>}
|
||||||
addOnAriaLabel={t('app.admin.vat_settings_modal.show_history')}
|
addOnAriaLabel={t('app.admin.vat_settings_modal.show_history')}
|
||||||
@ -132,6 +133,7 @@ export const VatSettingsModal: React.FC<VatSettingsModalProps> = ({ isOpen, togg
|
|||||||
<FormInput register={register}
|
<FormInput register={register}
|
||||||
id="invoice_VAT-rate_Product"
|
id="invoice_VAT-rate_Product"
|
||||||
type='number'
|
type='number'
|
||||||
|
step={0.001}
|
||||||
label={t('app.admin.vat_settings_modal.VAT_rate_product')}
|
label={t('app.admin.vat_settings_modal.VAT_rate_product')}
|
||||||
addOn={<ClockCounterClockwise size={24}/>}
|
addOn={<ClockCounterClockwise size={24}/>}
|
||||||
addOnAriaLabel={t('app.admin.vat_settings_modal.show_history')}
|
addOnAriaLabel={t('app.admin.vat_settings_modal.show_history')}
|
||||||
@ -143,6 +145,7 @@ export const VatSettingsModal: React.FC<VatSettingsModalProps> = ({ isOpen, togg
|
|||||||
<FormInput register={register}
|
<FormInput register={register}
|
||||||
id="invoice_VAT-rate_Event"
|
id="invoice_VAT-rate_Event"
|
||||||
type='number'
|
type='number'
|
||||||
|
step={0.001}
|
||||||
label={t('app.admin.vat_settings_modal.VAT_rate_event')}
|
label={t('app.admin.vat_settings_modal.VAT_rate_event')}
|
||||||
addOn={<ClockCounterClockwise size={24}/>}
|
addOn={<ClockCounterClockwise size={24}/>}
|
||||||
addOnAriaLabel={t('app.admin.vat_settings_modal.show_history')}
|
addOnAriaLabel={t('app.admin.vat_settings_modal.show_history')}
|
||||||
@ -154,6 +157,7 @@ export const VatSettingsModal: React.FC<VatSettingsModalProps> = ({ isOpen, togg
|
|||||||
<FormInput register={register}
|
<FormInput register={register}
|
||||||
id="invoice_VAT-rate_Machine"
|
id="invoice_VAT-rate_Machine"
|
||||||
type='number'
|
type='number'
|
||||||
|
step={0.001}
|
||||||
label={t('app.admin.vat_settings_modal.VAT_rate_machine')}
|
label={t('app.admin.vat_settings_modal.VAT_rate_machine')}
|
||||||
addOn={<ClockCounterClockwise size={24}/>}
|
addOn={<ClockCounterClockwise size={24}/>}
|
||||||
addOnAriaLabel={t('app.admin.vat_settings_modal.show_history')}
|
addOnAriaLabel={t('app.admin.vat_settings_modal.show_history')}
|
||||||
@ -165,6 +169,7 @@ export const VatSettingsModal: React.FC<VatSettingsModalProps> = ({ isOpen, togg
|
|||||||
<FormInput register={register}
|
<FormInput register={register}
|
||||||
id="invoice_VAT-rate_Subscription"
|
id="invoice_VAT-rate_Subscription"
|
||||||
type='number'
|
type='number'
|
||||||
|
step={0.001}
|
||||||
label={t('app.admin.vat_settings_modal.VAT_rate_subscription')}
|
label={t('app.admin.vat_settings_modal.VAT_rate_subscription')}
|
||||||
addOn={<ClockCounterClockwise size={24}/>}
|
addOn={<ClockCounterClockwise size={24}/>}
|
||||||
addOnAriaLabel={t('app.admin.vat_settings_modal.show_history')}
|
addOnAriaLabel={t('app.admin.vat_settings_modal.show_history')}
|
||||||
@ -176,6 +181,7 @@ export const VatSettingsModal: React.FC<VatSettingsModalProps> = ({ isOpen, togg
|
|||||||
<FormInput register={register}
|
<FormInput register={register}
|
||||||
id="invoice_VAT-rate_Space"
|
id="invoice_VAT-rate_Space"
|
||||||
type='number'
|
type='number'
|
||||||
|
step={0.001}
|
||||||
label={t('app.admin.vat_settings_modal.VAT_rate_space')}
|
label={t('app.admin.vat_settings_modal.VAT_rate_space')}
|
||||||
addOn={<ClockCounterClockwise size={24}/>}
|
addOn={<ClockCounterClockwise size={24}/>}
|
||||||
addOnAriaLabel={t('app.admin.vat_settings_modal.show_history')}
|
addOnAriaLabel={t('app.admin.vat_settings_modal.show_history')}
|
||||||
@ -187,6 +193,7 @@ export const VatSettingsModal: React.FC<VatSettingsModalProps> = ({ isOpen, togg
|
|||||||
<FormInput register={register}
|
<FormInput register={register}
|
||||||
id="invoice_VAT-rate_Training"
|
id="invoice_VAT-rate_Training"
|
||||||
type='number'
|
type='number'
|
||||||
|
step={0.001}
|
||||||
label={t('app.admin.vat_settings_modal.VAT_rate_training')}
|
label={t('app.admin.vat_settings_modal.VAT_rate_training')}
|
||||||
addOn={<ClockCounterClockwise size={24}/>}
|
addOn={<ClockCounterClockwise size={24}/>}
|
||||||
addOnAriaLabel={t('app.admin.vat_settings_modal.show_history')}
|
addOnAriaLabel={t('app.admin.vat_settings_modal.show_history')}
|
||||||
|
@ -59,7 +59,8 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
|
|||||||
spaces: isSelectAll('spaces', scope),
|
spaces: isSelectAll('spaces', scope),
|
||||||
externals: isSelectAll('externals', scope),
|
externals: isSelectAll('externals', scope),
|
||||||
evt: filter.evt,
|
evt: filter.evt,
|
||||||
dispo: filter.dispo
|
dispo: filter.dispo,
|
||||||
|
reserved: filter.reserved
|
||||||
});
|
});
|
||||||
scope.machinesGroupByCategory.forEach(c => c.checked = _.every(c.machines, 'checked'));
|
scope.machinesGroupByCategory.forEach(c => c.checked = _.every(c.machines, 'checked'));
|
||||||
// remove all
|
// remove all
|
||||||
@ -319,7 +320,7 @@ Application.Controllers.controller('CalendarController', ['$scope', '$state', '$
|
|||||||
const t = $scope.trainings.filter(t => t.checked).map(t => t.id);
|
const t = $scope.trainings.filter(t => t.checked).map(t => t.id);
|
||||||
const m = $scope.machines.filter(m => m.checked).map(m => m.id);
|
const m = $scope.machines.filter(m => m.checked).map(m => m.id);
|
||||||
const s = $scope.spaces.filter(s => s.checked).map(s => s.id);
|
const s = $scope.spaces.filter(s => s.checked).map(s => s.id);
|
||||||
return { t, m, s, evt: $scope.filter.evt, dispo: $scope.filter.dispo };
|
return { t, m, s, evt: $scope.filter.evt, dispo: $scope.filter.dispo, reserved: $scope.filter.reserved };
|
||||||
};
|
};
|
||||||
|
|
||||||
const availabilitySourceUrl = () => `/api/availabilities/public?${$.param(getFilter())}`;
|
const availabilitySourceUrl = () => `/api/availabilities/public?${$.param(getFilter())}`;
|
||||||
|
@ -8,9 +8,16 @@
|
|||||||
&.clicked {
|
&.clicked {
|
||||||
color: var(--secondary-dark);
|
color: var(--secondary-dark);
|
||||||
}
|
}
|
||||||
|
&.canceled {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.slot-details {
|
.slot-details {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
|
&.canceled {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.fab-popover {
|
.fab-popover {
|
||||||
|
@ -132,7 +132,12 @@ class Invoice < PaymentDocument
|
|||||||
end
|
end
|
||||||
|
|
||||||
def main_item
|
def main_item
|
||||||
invoice_items.where(main: true).first
|
main = invoice_items.where(main: true).first
|
||||||
|
if main.nil?
|
||||||
|
main = invoice_items.order(id: :asc).first
|
||||||
|
main&.update(main: true)
|
||||||
|
end
|
||||||
|
main
|
||||||
end
|
end
|
||||||
|
|
||||||
def other_items
|
def other_items
|
||||||
|
@ -169,6 +169,6 @@ class Accounting::AccountingService
|
|||||||
|
|
||||||
diff = debit_sum - credit_sum
|
diff = debit_sum - credit_sum
|
||||||
fixable_line = lines.filter { |l| l[:line_type] == 'payment' }.last
|
fixable_line = lines.filter { |l| l[:line_type] == 'payment' }.last
|
||||||
fixable_line.credit += diff
|
fixable_line[:credit] += diff
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# From this migration, ths object_type and object_id columns in InvoiceItem won't be able to be null anymore
|
||||||
|
# This will prevent issues while building the accounting data, and ensure data integrity
|
||||||
|
class AddNotNullToInvoiceItemsObject < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
change_column_null :invoice_items, :object_type, false
|
||||||
|
change_column_null :invoice_items, :object_id, false
|
||||||
|
end
|
||||||
|
end
|
@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2022_12_27_141529) do
|
ActiveRecord::Schema.define(version: 2023_01_06_081943) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "fuzzystrmatch"
|
enable_extension "fuzzystrmatch"
|
||||||
@ -331,8 +331,8 @@ ActiveRecord::Schema.define(version: 2022_12_27_141529) do
|
|||||||
t.text "description"
|
t.text "description"
|
||||||
t.integer "invoice_item_id"
|
t.integer "invoice_item_id"
|
||||||
t.string "footprint"
|
t.string "footprint"
|
||||||
t.string "object_type"
|
t.string "object_type", null: false
|
||||||
t.bigint "object_id"
|
t.bigint "object_id", null: false
|
||||||
t.boolean "main"
|
t.boolean "main"
|
||||||
t.index ["invoice_id"], name: "index_invoice_items_on_invoice_id"
|
t.index ["invoice_id"], name: "index_invoice_items_on_invoice_id"
|
||||||
t.index ["object_type", "object_id"], name: "index_invoice_items_on_object_type_and_object_id"
|
t.index ["object_type", "object_id"], name: "index_invoice_items_on_object_type_and_object_id"
|
||||||
|
64
lib/tasks/fablab/fix_invoice_items.rake
Normal file
64
lib/tasks/fablab/fix_invoice_items.rake
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'integrity/archive_helper'
|
||||||
|
|
||||||
|
# This take will ensure data integrity for invoices_items.
|
||||||
|
# Due a an unknown bug, some invoice items may not contains the reference to their object.
|
||||||
|
# This task will re-associate these items with their reservation/subscription/etc
|
||||||
|
namespace :fablab do
|
||||||
|
desc 'Associate the invoice_items w/o object'
|
||||||
|
task fix_invoice_items: :environment do |_task, _args|
|
||||||
|
next unless InvoiceItem.where(object_type: nil)
|
||||||
|
.or(InvoiceItem.where(object_id: nil))
|
||||||
|
.count
|
||||||
|
.positive?
|
||||||
|
|
||||||
|
include ActionView::Helpers::NumberHelper
|
||||||
|
|
||||||
|
# check the footprints and save the archives
|
||||||
|
Integrity::ArchiveHelper.check_footprints
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
periods = Integrity::ArchiveHelper.backup_and_remove_periods
|
||||||
|
|
||||||
|
# fix invoice items data
|
||||||
|
InvoiceItem.where(object_type: nil)
|
||||||
|
.or(InvoiceItem.where(object_id: nil))
|
||||||
|
.find_each do |ii|
|
||||||
|
invoice = ii.invoice
|
||||||
|
other_items = invoice.invoice_items.where.not(id: ii.id)
|
||||||
|
puts "\e[4;33mFound an invalid InvoiceItem\e[0m"
|
||||||
|
puts '=============================================='
|
||||||
|
puts "Invoice #{invoice.id} (# #{invoice.reference})"
|
||||||
|
puts "Total: #{number_to_currency(invoice.total / 100.0)}"
|
||||||
|
puts "Customer: #{invoice.invoicing_profile.full_name} (#{invoice.invoicing_profile.email})"
|
||||||
|
puts "Operator: #{invoice.operator_profile&.user&.profile&.full_name} (#{invoice.operator_profile&.user&.email})"
|
||||||
|
puts "Date: #{invoice.created_at}"
|
||||||
|
puts '=============================================='
|
||||||
|
puts "Concerned item: #{ii.id}"
|
||||||
|
puts "Item subject: #{ii.description}."
|
||||||
|
other_items.find_each do |oii|
|
||||||
|
puts '=============================================='
|
||||||
|
puts "Other item: #{oii.description} (#{oii.id})"
|
||||||
|
puts "Other item object: #{oii.object_type} #{oii.object_id}"
|
||||||
|
puts "Other item slots: #{oii.object.try(:slots)&.map { |s| "#{s.start_at} - #{s.end_at}" }}"
|
||||||
|
print "\e[1;34m[ ? ]\e[0m Associate the item with #{oii.object_type} #{oii.object_id} ? (y/N) > "
|
||||||
|
confirm = $stdin.gets.chomp
|
||||||
|
ii.update(object_id: oii.object_id, object_type: oii.object_type) if confirm == 'y'
|
||||||
|
end
|
||||||
|
ii.reload
|
||||||
|
if ii.object_id.nil? || ii.object_type.nil?
|
||||||
|
puts "\n\e[0;31mERROR\e[0m: InvoiceItem(#{ii.id}) was not associated with an object. Please open a rails console " \
|
||||||
|
"to manually fix the issue using `InvoiceItem.find(#{ii.id}.update(object_id: XXX, object_type: 'XXX')`.\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# chain records
|
||||||
|
puts 'Chaining all record. This may take a while...'
|
||||||
|
InvoiceItem.order(:id).all.each(&:chain_record)
|
||||||
|
Invoice.order(:id).all.each(&:chain_record)
|
||||||
|
|
||||||
|
# re-create all archives from the memory dump
|
||||||
|
Integrity::ArchiveHelper.restore_periods(periods)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -12,9 +12,7 @@ namespace :fablab do
|
|||||||
|
|
||||||
desc 'add missing VAT rate to history'
|
desc 'add missing VAT rate to history'
|
||||||
task :add_vat_rate, %i[rate date] => :environment do |_task, args|
|
task :add_vat_rate, %i[rate date] => :environment do |_task, args|
|
||||||
unless args.rate && args.date
|
raise 'Missing argument. Usage exemple: rails fablab:setup:add_vat_rate[20,2014-01-01]. Use 0 to disable' unless args.rate && args.date
|
||||||
raise 'Missing argument. Usage exemple: rails fablab:setup:add_vat_rate[20,2014-01-01]. Use 0 to disable'
|
|
||||||
end
|
|
||||||
|
|
||||||
if args.rate == '0'
|
if args.rate == '0'
|
||||||
setting = Setting.find_by(name: 'invoice_VAT-active')
|
setting = Setting.find_by(name: 'invoice_VAT-active')
|
||||||
@ -129,7 +127,7 @@ namespace :fablab do
|
|||||||
|
|
||||||
desc 'generate acconting lines'
|
desc 'generate acconting lines'
|
||||||
task build_accounting_lines: :environment do
|
task build_accounting_lines: :environment do
|
||||||
start_date = Invoice.order(created_at: :asc).first&.created_at
|
start_date = Invoice.order(created_at: :asc).first&.created_at || DateTime.current
|
||||||
end_date = DateTime.current
|
end_date = DateTime.current
|
||||||
AccountingLine.where(date: start_date..end_date).destroy_all
|
AccountingLine.where(date: start_date..end_date).destroy_all
|
||||||
Accounting::AccountingService.new.build(start_date&.beginning_of_day, end_date.end_of_day)
|
Accounting::AccountingService.new.build(start_date&.beginning_of_day, end_date.end_of_day)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "fab-manager",
|
"name": "fab-manager",
|
||||||
"version": "5.6.0",
|
"version": "5.6.1",
|
||||||
"description": "Fab-manager is the FabLab management solution. It provides a comprehensive, web-based, open-source tool to simplify your administrative tasks and your marker's projects.",
|
"description": "Fab-manager is the FabLab management solution. It provides a comprehensive, web-based, open-source tool to simplify your administrative tasks and your marker's projects.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"fablab",
|
"fablab",
|
||||||
|
@ -26,6 +26,7 @@ describe('VatSettingsModal', () => {
|
|||||||
expect(screen.getByLabelText(/app.admin.vat_settings_modal.enable_VAT/)).toBeChecked();
|
expect(screen.getByLabelText(/app.admin.vat_settings_modal.enable_VAT/)).toBeChecked();
|
||||||
});
|
});
|
||||||
fireEvent.click(screen.getByRole('button', { name: /app.admin.vat_settings_modal.advanced/, hidden: true }));
|
fireEvent.click(screen.getByRole('button', { name: /app.admin.vat_settings_modal.advanced/, hidden: true }));
|
||||||
|
expect(screen.getByLabelText(/app.admin.vat_settings_modal.VAT_rate/, { selector: '#invoice_VAT-rate' })).toBeInTheDocument();
|
||||||
expect(screen.getByLabelText(/app.admin.vat_settings_modal.VAT_rate_product/)).toBeInTheDocument();
|
expect(screen.getByLabelText(/app.admin.vat_settings_modal.VAT_rate_product/)).toBeInTheDocument();
|
||||||
expect(screen.getByLabelText(/app.admin.vat_settings_modal.VAT_rate_event/)).toBeInTheDocument();
|
expect(screen.getByLabelText(/app.admin.vat_settings_modal.VAT_rate_event/)).toBeInTheDocument();
|
||||||
expect(screen.getByLabelText(/app.admin.vat_settings_modal.VAT_rate_machine/)).toBeInTheDocument();
|
expect(screen.getByLabelText(/app.admin.vat_settings_modal.VAT_rate_machine/)).toBeInTheDocument();
|
||||||
@ -44,4 +45,14 @@ describe('VatSettingsModal', () => {
|
|||||||
expect(screen.getByRole('heading', { name: /app.admin.setting_history_modal.title/, hidden: true })).toBeInTheDocument();
|
expect(screen.getByRole('heading', { name: /app.admin.setting_history_modal.title/, hidden: true })).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('input 3 decimals rate', async () => {
|
||||||
|
render(<VatSettingsModal isOpen={true} toggleModal={toggleModal} onError={onError} onSuccess={onSuccess} />);
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByLabelText(/app.admin.vat_settings_modal.enable_VAT/)).toBeChecked();
|
||||||
|
});
|
||||||
|
const input = screen.getByLabelText(/app.admin.vat_settings_modal.VAT_rate/, { selector: '#invoice_VAT-rate' });
|
||||||
|
fireEvent.change(input, { target: { value: 14.976 } });
|
||||||
|
expect(input).toHaveValue(14.976);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
13
yarn.lock
13
yarn.lock
@ -7696,9 +7696,9 @@ json-stable-stringify-without-jsonify@^1.0.1:
|
|||||||
integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
|
integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
|
||||||
|
|
||||||
json5@^1.0.1:
|
json5@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
|
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593"
|
||||||
integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
|
integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==
|
||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
|
|
||||||
@ -8059,16 +8059,11 @@ minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatc
|
|||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion "^1.1.7"
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
minimist@^1.2.0:
|
minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6:
|
||||||
version "1.2.7"
|
version "1.2.7"
|
||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18"
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18"
|
||||||
integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==
|
integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==
|
||||||
|
|
||||||
minimist@^1.2.5, minimist@^1.2.6:
|
|
||||||
version "1.2.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
|
||||||
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
|
|
||||||
|
|
||||||
mkdirp@^0.5.5:
|
mkdirp@^0.5.5:
|
||||||
version "0.5.5"
|
version "0.5.5"
|
||||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
|
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user