99 lines
4.5 KiB
Python
99 lines
4.5 KiB
Python
import re
|
|
|
|
with open('templates/expenses/expense_form.html', 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
|
|
# 1. Add x-data
|
|
content = content.replace('<form method="post" enctype="multipart/form-data">', '<form method="post" enctype="multipart/form-data" x-data="expenseForm()">')
|
|
|
|
# 2. Add x-show to responsible person container
|
|
content = content.replace('<div id="responsible-person-container"', '<div id="responsible-person-container" x-show="isReimbursement"')
|
|
|
|
# 3. Add x-show to new vendor fields
|
|
# We need to bind the toggle-new-vendor checkbox
|
|
content = content.replace('<input type="checkbox" id="toggle-new-vendor"', '<input type="checkbox" id="toggle-new-vendor" x-model="isNewVendor"')
|
|
content = content.replace('<div id="new-vendor-fields"', '<div id="new-vendor-fields" x-show="isNewVendor"')
|
|
content = content.replace('style="display: none;', 'style="display: none;" x-cloak ')
|
|
|
|
# Add x-cloak style to head or just use x-show directly
|
|
# Actually replacing the display: none might be tricky, let's just do x-show.
|
|
content = content.replace('<div id="new-vendor-fields" style="display: none;', '<div id="new-vendor-fields" x-cloak x-show="isNewVendor" style="')
|
|
content = content.replace('<div id="responsible-person-container" x-show="isReimbursement" style="margin-top:0.5rem;">', '<div id="responsible-person-container" x-cloak x-show="isReimbursement" style="margin-top:0.5rem;">')
|
|
|
|
# Replace the giant vanilla JS <script> with Alpine
|
|
# The original script starts at <script> around line 242 and ends at </script> around line 875
|
|
script_pattern = re.compile(r'<script>\s*document\.querySelectorAll.*?</script>', re.DOTALL)
|
|
|
|
alpine_script = """
|
|
<script>
|
|
document.addEventListener('alpine:init', () => {
|
|
Alpine.data('expenseForm', () => ({
|
|
isReimbursement: document.getElementById('id_is_reimbursement')?.checked || false,
|
|
isNewVendor: false,
|
|
amountClp: document.getElementById('id_amount_clp')?.value || '',
|
|
taxType: document.getElementById('id_tax_type')?.value || 'EXENTO',
|
|
taxAmountClp: document.getElementById('id_tax_amount_clp')?.value || '0',
|
|
totalAmountClp: document.getElementById('id_total_amount_clp')?.value || '0',
|
|
newVendorRut: '',
|
|
rutError: false,
|
|
|
|
formatAmount() {
|
|
let cleanVal = this.amountClp.replace(/\./g, '').replace(/\D/g, '');
|
|
if (cleanVal) {
|
|
this.amountClp = cleanVal.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, ".");
|
|
} else {
|
|
this.amountClp = '';
|
|
}
|
|
},
|
|
|
|
async calculateTax() {
|
|
let cleanVal = this.amountClp.replace(/\./g, '');
|
|
let amount = parseFloat(cleanVal) || 0;
|
|
|
|
if (amount > 0 && this.taxType) {
|
|
try {
|
|
const response = await fetch(`/api/v1/sii/calculate-tax?amount=${amount}&tax_type=${this.taxType}`);
|
|
const data = await response.json();
|
|
this.taxAmountClp = data.tax_amount.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, ".");
|
|
this.totalAmountClp = data.total_amount.toString().replace(/\\B(?=(\\d{3})+(?!\\d))/g, ".");
|
|
} catch (e) {
|
|
console.error('Error calculating tax', e);
|
|
}
|
|
} else {
|
|
this.taxAmountClp = '0';
|
|
this.totalAmountClp = '0';
|
|
}
|
|
},
|
|
|
|
async validateRut() {
|
|
if (!this.newVendorRut) {
|
|
this.rutError = false;
|
|
return;
|
|
}
|
|
try {
|
|
const response = await fetch(`/api/v1/sii/validate-rut?rut=${this.newVendorRut}`);
|
|
const data = await response.json();
|
|
if (data.is_valid) {
|
|
this.newVendorRut = data.formatted_rut;
|
|
this.rutError = false;
|
|
} else {
|
|
this.rutError = true;
|
|
alert(data.error_message || "RUT inválido");
|
|
}
|
|
} catch (e) {
|
|
console.error('Error validating RUT', e);
|
|
}
|
|
}
|
|
}));
|
|
});
|
|
</script>
|
|
<style>
|
|
[x-cloak] { display: none !important; }
|
|
</style>
|
|
"""
|
|
|
|
content = script_pattern.sub(alpine_script, content)
|
|
|
|
with open('templates/expenses/expense_form.html', 'w', encoding='utf-8') as f:
|
|
f.write(content)
|