mirror of
				https://github.com/crater-invoice/crater.git
				synced 2025-10-29 04:31:08 -04:00 
			
		
		
		
	Compare commits
	
		
			11 Commits
		
	
	
		
			dark-fix-i
			...
			tax-calcul
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 9608ab6207 | |||
| c14eb1b414 | |||
| 6d0edb4b5a | |||
| 0dc8941975 | |||
| f11437ce63 | |||
| dbd75bbe68 | |||
| 4fc67c74e4 | |||
| 27660c6bce | |||
| 05d5ce26fd | |||
| 393fe20010 | |||
| 57bdbd2897 | 
							
								
								
									
										9
									
								
								.github/workflows/uffizzi-build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.github/workflows/uffizzi-build.yml
									
									
									
									
										vendored
									
									
								
							| @ -14,8 +14,6 @@ jobs: | ||||
|     steps: | ||||
|       - name: Checkout git repo | ||||
|         uses: actions/checkout@v3 | ||||
|       - name: Set up Docker Buildx | ||||
|         uses: docker/setup-buildx-action@v2         | ||||
|       - name: Generate UUID image name | ||||
|         id: uuid | ||||
|         run: echo "UUID_TAG_APP=$(uuidgen)" >> $GITHUB_ENV | ||||
| @ -33,11 +31,10 @@ jobs: | ||||
|           tags: ${{ steps.meta.outputs.tags }} | ||||
|           labels: ${{ steps.meta.outputs.labels }} | ||||
|           file: ./uffizzi/Dockerfile | ||||
|           cache-from: type=gha | ||||
|           cache-to: type=gha,mode=max | ||||
|  | ||||
|  | ||||
|   build-nginx: | ||||
|     needs:  | ||||
|       - build-application | ||||
|     name: Build and Push `nginx` | ||||
|     runs-on: ubuntu-latest | ||||
|     if: ${{ github.event_name != 'pull_request' || github.event.action != 'closed' }} | ||||
| @ -65,6 +62,8 @@ jobs: | ||||
|           tags: ${{ steps.meta.outputs.tags }} | ||||
|           labels: ${{ steps.meta.outputs.labels }} | ||||
|           file: ./uffizzi/nginx/Dockerfile | ||||
|           build-args: | | ||||
|             BASE_IMAGE=${{ needs.build-application.outputs.tags }} | ||||
|           cache-from: type=gha | ||||
|           cache-to: type=gha,mode=max | ||||
|  | ||||
|  | ||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -17,3 +17,4 @@ Homestead.yaml | ||||
| /public/docs | ||||
| /.scribe | ||||
| !storage/fonts/.gitkeep | ||||
| .DS_Store | ||||
|  | ||||
| @ -55,7 +55,7 @@ class CreateTemplateCommand extends Command | ||||
|         copy(public_path("/build/img/PDF/{$type}1.png"), public_path("/build/img/PDF/{$templateName}.png")); | ||||
|         copy(resource_path("/static/img/PDF/{$type}1.png"), resource_path("/static/img/PDF/{$templateName}.png")); | ||||
|  | ||||
|         $path = resource_path("app/pdf/{$type}/{$templateName}.blade.php"); | ||||
|         $path = resource_path("views/app/pdf/{$type}/{$templateName}.blade.php"); | ||||
|         $type = ucfirst($type); | ||||
|         $this->info("{$type} Template created successfully at ".$path); | ||||
|  | ||||
|  | ||||
| @ -45,15 +45,21 @@ class EstimatesRequest extends FormRequest | ||||
|                 'nullable' | ||||
|             ], | ||||
|             'discount' => [ | ||||
|                 'numeric', | ||||
|                 'required', | ||||
|             ], | ||||
|             'discount_val' => [ | ||||
|                 'integer', | ||||
|                 'required', | ||||
|             ], | ||||
|             'sub_total' => [ | ||||
|                 'integer', | ||||
|                 'required', | ||||
|             ], | ||||
|             'total' => [ | ||||
|                 'integer', | ||||
|                 'numeric', | ||||
|                 'max:99999999', | ||||
|                 'required', | ||||
|             ], | ||||
|             'tax' => [ | ||||
| @ -77,9 +83,11 @@ class EstimatesRequest extends FormRequest | ||||
|                 'required', | ||||
|             ], | ||||
|             'items.*.quantity' => [ | ||||
|                 'integer', | ||||
|                 'required', | ||||
|             ], | ||||
|             'items.*.price' => [ | ||||
|                 'integer', | ||||
|                 'required', | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
| @ -45,15 +45,21 @@ class InvoicesRequest extends FormRequest | ||||
|                 'nullable' | ||||
|             ], | ||||
|             'discount' => [ | ||||
|                 'numeric', | ||||
|                 'required', | ||||
|             ], | ||||
|             'discount_val' => [ | ||||
|                 'integer', | ||||
|                 'required', | ||||
|             ], | ||||
|             'sub_total' => [ | ||||
|                 'integer', | ||||
|                 'required', | ||||
|             ], | ||||
|             'total' => [ | ||||
|                 'integer', | ||||
|                 'numeric', | ||||
|                 'max:99999999', | ||||
|                 'required', | ||||
|             ], | ||||
|             'tax' => [ | ||||
| @ -77,9 +83,11 @@ class InvoicesRequest extends FormRequest | ||||
|                 'required', | ||||
|             ], | ||||
|             'items.*.quantity' => [ | ||||
|                 'integer', | ||||
|                 'required', | ||||
|             ], | ||||
|             'items.*.price' => [ | ||||
|                 'integer', | ||||
|                 'required', | ||||
|             ], | ||||
|         ]; | ||||
|  | ||||
| @ -43,15 +43,21 @@ class RecurringInvoiceRequest extends FormRequest | ||||
|                 'nullable' | ||||
|             ], | ||||
|             'discount' => [ | ||||
|                 'numeric', | ||||
|                 'required', | ||||
|             ], | ||||
|             'discount_val' => [ | ||||
|                 'integer', | ||||
|                 'required', | ||||
|             ], | ||||
|             'sub_total' => [ | ||||
|                 'integer', | ||||
|                 'required', | ||||
|             ], | ||||
|             'total' => [ | ||||
|                 'integer', | ||||
|                 'numeric', | ||||
|                 'max:99999999', | ||||
|                 'required', | ||||
|             ], | ||||
|             'tax' => [ | ||||
|  | ||||
| @ -27,6 +27,7 @@ return [ | ||||
|             'tokenizer', | ||||
|             'JSON', | ||||
|             'cURL', | ||||
|             'zip', | ||||
|         ], | ||||
|         'apache' => [ | ||||
|             'mod_rewrite', | ||||
|  | ||||
| @ -271,23 +271,19 @@ const price = computed({ | ||||
|     } else { | ||||
|       updateItemAttribute('price', newValue) | ||||
|     } | ||||
|     setDiscount() | ||||
|   }, | ||||
| }) | ||||
|  | ||||
| const subtotal = computed(() => props.itemData.price * props.itemData.quantity) | ||||
| const subtotal = computed(() => Math.round(props.itemData.price * props.itemData.quantity)) | ||||
|  | ||||
| const discount = computed({ | ||||
|   get: () => { | ||||
|     return props.itemData.discount | ||||
|   }, | ||||
|   set: (newValue) => { | ||||
|     if (props.itemData.discount_type === 'percentage') { | ||||
|       updateItemAttribute('discount_val', (subtotal.value * newValue) / 100) | ||||
|     } else { | ||||
|       updateItemAttribute('discount_val', Math.round(newValue * 100)) | ||||
|     } | ||||
|  | ||||
|     updateItemAttribute('discount', newValue) | ||||
|     setDiscount() | ||||
|   }, | ||||
| }) | ||||
|  | ||||
| @ -313,7 +309,7 @@ const showRemoveButton = computed(() => { | ||||
| const totalSimpleTax = computed(() => { | ||||
|   return Math.round( | ||||
|     sumBy(props.itemData.taxes, function (tax) { | ||||
|       if (!tax.compound_tax) { | ||||
|       if (tax.amount) { | ||||
|         return tax.amount | ||||
|       } | ||||
|       return 0 | ||||
| @ -321,18 +317,7 @@ const totalSimpleTax = computed(() => { | ||||
|   ) | ||||
| }) | ||||
|  | ||||
| const totalCompoundTax = computed(() => { | ||||
|   return Math.round( | ||||
|     sumBy(props.itemData.taxes, function (tax) { | ||||
|       if (tax.compound_tax) { | ||||
|         return tax.amount | ||||
|       } | ||||
|       return 0 | ||||
|     }) | ||||
|   ) | ||||
| }) | ||||
|  | ||||
| const totalTax = computed(() => totalSimpleTax.value + totalCompoundTax.value) | ||||
| const totalTax = computed(() => totalSimpleTax.value) | ||||
|  | ||||
| const rules = { | ||||
|   name: { | ||||
| @ -416,6 +401,16 @@ function updateTax(data) { | ||||
|   syncItemToStore() | ||||
| } | ||||
|  | ||||
| function setDiscount() { | ||||
|   const newValue = props.store[props.storeProp].items[props.index].discount | ||||
|  | ||||
|   if (props.itemData.discount_type === 'percentage'){ | ||||
|     updateItemAttribute('discount_val', Math.round((subtotal.value * newValue) / 100)) | ||||
|   }else{ | ||||
|     updateItemAttribute('discount_val', Math.round(newValue * 100)) | ||||
|   } | ||||
| } | ||||
|  | ||||
| function searchVal(val) { | ||||
|   updateItemAttribute('name', val) | ||||
| } | ||||
| @ -485,10 +480,12 @@ function syncItemToStore() { | ||||
|     total: total.value, | ||||
|     sub_total: subtotal.value, | ||||
|     totalSimpleTax: totalSimpleTax.value, | ||||
|     totalCompoundTax: totalCompoundTax.value, | ||||
|     totalTax: totalTax.value, | ||||
|     tax: totalTax.value, | ||||
|     taxes: [...itemTaxes], | ||||
|     tax_type_ids: itemTaxes.flatMap(_t => | ||||
|       _t.tax_type_id ? _t.tax_type_id : [], | ||||
|     ), | ||||
|   } | ||||
|  | ||||
|   props.store.updateItem(data) | ||||
|  | ||||
| @ -146,14 +146,14 @@ const filteredTypes = computed(() => { | ||||
| }) | ||||
|  | ||||
| const taxAmount = computed(() => { | ||||
|   if (localTax.compound_tax && props.discountedTotal) { | ||||
|     return ((props.discountedTotal + props.totalTax) * localTax.percent) / 100 | ||||
|   } | ||||
|  | ||||
|   if (props.discountedTotal && localTax.percent) { | ||||
|     const taxPerItemEnabled = props.store[props.storeProp].tax_per_item === 'YES' | ||||
|     const discountPerItemEnabled = props.store[props.storeProp].discount_per_item === 'YES' | ||||
|     if (taxPerItemEnabled && !discountPerItemEnabled){ | ||||
|       return getTaxAmount() | ||||
|     } | ||||
|     return (props.discountedTotal * localTax.percent) / 100 | ||||
|   } | ||||
|  | ||||
|   return 0 | ||||
| }) | ||||
|  | ||||
| @ -171,6 +171,13 @@ watch( | ||||
|   } | ||||
| ) | ||||
|  | ||||
| watch( | ||||
|   () => taxAmount.value, | ||||
|   () => { | ||||
|     updateRowTax() | ||||
|   }, | ||||
| ) | ||||
|  | ||||
| // Set SelectedTax | ||||
| if (props.taxData.tax_type_id > 0) { | ||||
|   selectedTax.value = taxTypeStore.taxTypes.find( | ||||
| @ -183,7 +190,6 @@ updateRowTax() | ||||
| function onSelectTax(val) { | ||||
|   localTax.percent = val.percent | ||||
|   localTax.tax_type_id = val.id | ||||
|   localTax.compound_tax = val.compound_tax | ||||
|   localTax.name = val.name | ||||
|  | ||||
|   updateRowTax() | ||||
| @ -220,6 +226,27 @@ function openTaxModal() { | ||||
| function removeTax(index) { | ||||
|   props.store.$patch((state) => { | ||||
|     state[props.storeProp].items[props.itemIndex].taxes.splice(index, 1) | ||||
|     state[props.storeProp].items[props.itemIndex].tax = 0 | ||||
|     state[props.storeProp].items[props.itemIndex].totalTax = 0 | ||||
|   }) | ||||
| } | ||||
|  | ||||
| function getTaxAmount() { | ||||
|   let total = 0 | ||||
|   let discount = 0 | ||||
|   const itemTotal = props.discountedTotal | ||||
|   const modelDiscount = props.store[props.storeProp].discount ? props.store[props.storeProp].discount : 0 | ||||
|   const type = props.store[props.storeProp].discount_type | ||||
|   if (modelDiscount > 0) { | ||||
|     props.store[props.storeProp].items.forEach((_i) => { | ||||
|       total += _i.total | ||||
|     }) | ||||
|     const proportion = (itemTotal / total).toFixed(2) | ||||
|     discount = type === 'fixed' ? modelDiscount * 100 : (total * modelDiscount) / 100 | ||||
|     const itemDiscount = Math.round(discount * proportion) | ||||
|     const discounted = itemTotal - itemDiscount | ||||
|     return Math.round((discounted * localTax.percent) / 100) | ||||
|   } | ||||
|   return Math.round((props.discountedTotal * localTax.percent) / 100) | ||||
| } | ||||
| </script> | ||||
|  | ||||
| @ -191,7 +191,7 @@ | ||||
| </template> | ||||
|  | ||||
| <script setup> | ||||
| import { computed, inject, ref } from 'vue' | ||||
| import { computed, inject, ref, watch } from 'vue' | ||||
| import Guid from 'guid' | ||||
| import Tax from './CreateTotalTaxes.vue' | ||||
| import TaxStub from '@/scripts/admin/stub/abilities' | ||||
| @ -227,19 +227,20 @@ const utils = inject('$utils') | ||||
|  | ||||
| const companyStore = useCompanyStore() | ||||
|  | ||||
| watch( | ||||
|   () => props.store[props.storeProp].items, | ||||
|   (val) => { | ||||
|     setDiscount() | ||||
|   }, { deep: true }, | ||||
| ) | ||||
|  | ||||
| const totalDiscount = computed({ | ||||
|   get: () => { | ||||
|     return props.store[props.storeProp].discount | ||||
|   }, | ||||
|   set: (newValue) => { | ||||
|     if (props.store[props.storeProp].discount_type === 'percentage') { | ||||
|       props.store[props.storeProp].discount_val = Math.round( | ||||
|         (props.store.getSubTotal * newValue) / 100 | ||||
|       ) | ||||
|     } else { | ||||
|       props.store[props.storeProp].discount_val = Math.round(newValue * 100) | ||||
|     } | ||||
|     props.store[props.storeProp].discount = newValue | ||||
|     setDiscount() | ||||
|   }, | ||||
| }) | ||||
|  | ||||
| @ -265,7 +266,7 @@ const itemWiseTaxes = computed(() => { | ||||
|         } else if (tax.tax_type_id) { | ||||
|           taxes.push({ | ||||
|             tax_type_id: tax.tax_type_id, | ||||
|             amount: tax.amount, | ||||
|             amount: Math.round(tax.amount), | ||||
|             percent: tax.percent, | ||||
|             name: tax.name, | ||||
|           }) | ||||
| @ -284,6 +285,19 @@ const defaultCurrency = computed(() => { | ||||
|   } | ||||
| }) | ||||
|  | ||||
| function setDiscount() { | ||||
|   const newValue = props.store[props.storeProp].discount | ||||
|  | ||||
|   if (props.store[props.storeProp].discount_type === 'percentage') { | ||||
|     props.store[props.storeProp].discount_val | ||||
|       = Math.round((props.store.getSubTotal * newValue) / 100) | ||||
|  | ||||
|     return | ||||
|   } | ||||
|  | ||||
|   props.store[props.storeProp].discount_val = Math.round(newValue * 100) | ||||
| } | ||||
|  | ||||
| function selectFixed() { | ||||
|   if (props.store[props.storeProp].discount_type === 'fixed') { | ||||
|     return | ||||
| @ -298,21 +312,18 @@ function selectPercentage() { | ||||
|   if (props.store[props.storeProp].discount_type === 'percentage'){ | ||||
|     return | ||||
|   } | ||||
|   props.store[props.storeProp].discount_val = | ||||
|     (props.store.getSubTotal * props.store[props.storeProp].discount) / 100 | ||||
|  | ||||
|   const val = Math.round(props.store[props.storeProp].discount * 100) / 100 | ||||
|  | ||||
|   props.store[props.storeProp].discount_val | ||||
|     = Math.round((props.store.getSubTotal * val) / 100) | ||||
|  | ||||
|   props.store[props.storeProp].discount_type = 'percentage' | ||||
| } | ||||
|  | ||||
| function onSelectTax(selectedTax) { | ||||
|   let amount = 0 | ||||
|  | ||||
|   if (selectedTax.compound_tax && props.store.getSubtotalWithDiscount) { | ||||
|     amount = Math.round( | ||||
|       ((props.store.getSubtotalWithDiscount + props.store.getTotalSimpleTax) * | ||||
|         selectedTax.percent) / | ||||
|         100 | ||||
|     ) | ||||
|   } else if (props.store.getSubtotalWithDiscount && selectedTax.percent) { | ||||
|   if (props.store.getSubtotalWithDiscount && selectedTax.percent) { | ||||
|     amount = Math.round( | ||||
|       (props.store.getSubtotalWithDiscount * selectedTax.percent) / 100 | ||||
|     ) | ||||
| @ -323,7 +334,6 @@ function onSelectTax(selectedTax) { | ||||
|     id: Guid.raw(), | ||||
|     name: selectedTax.name, | ||||
|     percent: selectedTax.percent, | ||||
|     compound_tax: selectedTax.compound_tax, | ||||
|     tax_type_id: selectedTax.id, | ||||
|     amount, | ||||
|   } | ||||
|  | ||||
| @ -77,17 +77,6 @@ | ||||
|               @input="v$.currentTaxType.description.$touch()" | ||||
|             /> | ||||
|           </BaseInputGroup> | ||||
|  | ||||
|           <BaseInputGroup | ||||
|             :label="$t('tax_types.compound_tax')" | ||||
|             variant="horizontal" | ||||
|             class="flex flex-row-reverse" | ||||
|           > | ||||
|             <BaseSwitch | ||||
|               v-model="taxTypeStore.currentTaxType.compound_tax" | ||||
|               class="flex items-center" | ||||
|             /> | ||||
|           </BaseInputGroup> | ||||
|         </BaseInputGrid> | ||||
|       </div> | ||||
|       <div | ||||
| @ -209,14 +198,7 @@ async function submitTaxTypeData() { | ||||
|  | ||||
| function SelectTax(taxData) { | ||||
|   let amount = 0 | ||||
|   if (taxData.compound_tax && estimateStore.getSubtotalWithDiscount) { | ||||
|     amount = Math.round( | ||||
|       ((estimateStore.getSubtotalWithDiscount + | ||||
|         estimateStore.getTotalSimpleTax) * | ||||
|         taxData.percent) / | ||||
|         100 | ||||
|     ) | ||||
|   } else if (estimateStore.getSubtotalWithDiscount && taxData.percent) { | ||||
|  if (estimateStore.getSubtotalWithDiscount && taxData.percent) { | ||||
|     amount = Math.round( | ||||
|       (estimateStore.getSubtotalWithDiscount * taxData.percent) / 100 | ||||
|     ) | ||||
| @ -226,7 +208,6 @@ function SelectTax(taxData) { | ||||
|     id: Guid.raw(), | ||||
|     name: taxData.name, | ||||
|     percent: taxData.percent, | ||||
|     compound_tax: taxData.compound_tax, | ||||
|     tax_type_id: taxData.id, | ||||
|     amount, | ||||
|   } | ||||
| @ -242,7 +223,6 @@ function selectItemTax(taxData) { | ||||
|       id: Guid.raw(), | ||||
|       name: taxData.name, | ||||
|       percent: taxData.percent, | ||||
|       compound_tax: taxData.compound_tax, | ||||
|       tax_type_id: taxData.id, | ||||
|     } | ||||
|     modalStore.refreshData(data) | ||||
|  | ||||
| @ -143,7 +143,8 @@ export const useEstimateStore = (useWindow = false) => { | ||||
|           axios | ||||
|             .get(`/api/v1/estimates/${id}`) | ||||
|             .then((response) => { | ||||
|               Object.assign(this.newEstimate, response.data.data) | ||||
|               this.setEstimateData(response.data.data) | ||||
|               this.setCustomerAddresses(this.newEstimate.customer) | ||||
|               resolve(response) | ||||
|             }) | ||||
|             .catch((err) => { | ||||
| @ -154,6 +155,41 @@ export const useEstimateStore = (useWindow = false) => { | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       setEstimateData(estimate) { | ||||
|         Object.assign(this.newEstimate, estimate) | ||||
|         if (this.newEstimate.tax_per_item === 'YES') { | ||||
|           this.newEstimate.items.forEach((_i) => { | ||||
|             if (_i.taxes && !_i.taxes.length){ | ||||
|               _i.taxes.push({ ...taxStub, id: Guid.raw() }) | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|         if (this.newEstimate.discount_per_item === 'YES') { | ||||
|           this.newEstimate.items.forEach((_i, index) => { | ||||
|             if (_i.discount_type === 'fixed'){ | ||||
|               this.newEstimate.items[index].discount = _i.discount / 100 | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|         else { | ||||
|           if (this.newEstimate.discount_type === 'fixed'){ | ||||
|             this.newEstimate.discount = this.newEstimate.discount / 100 | ||||
|           } | ||||
|         } | ||||
|       }, | ||||
|  | ||||
|       setCustomerAddresses(customer) { | ||||
|         const customer_business = customer.customer_business | ||||
|  | ||||
|         if (customer_business?.billing_address){ | ||||
|           this.newEstimate.customer.billing_address = customer_business.billing_address | ||||
|         } | ||||
|  | ||||
|         if (customer_business?.shipping_address){ | ||||
|           this.newEstimate.customer.shipping_address = customer_business.shipping_address | ||||
|         } | ||||
|       }, | ||||
|  | ||||
|       addSalesTaxUs() { | ||||
|         const taxTypeStore = useTaxTypeStore() | ||||
|         let salesTax = { ...taxStub } | ||||
|  | ||||
| @ -133,8 +133,8 @@ export const useInvoiceStore = (useWindow = false) => { | ||||
|           axios | ||||
|             .get(`/api/v1/invoices/${id}`) | ||||
|             .then((response) => { | ||||
|               Object.assign(this.newInvoice, response.data.data) | ||||
|               this.newInvoice.customer = response.data.data.customer | ||||
|               this.setInvoiceData(response.data.data) | ||||
|               this.setCustomerAddresses(this.newInvoice.customer) | ||||
|               resolve(response) | ||||
|             }) | ||||
|             .catch((err) => { | ||||
| @ -144,6 +144,38 @@ export const useInvoiceStore = (useWindow = false) => { | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       setInvoiceData(invoice) { | ||||
|         Object.assign(this.newInvoice, invoice) | ||||
|  | ||||
|         if (this.newInvoice.tax_per_item === 'YES') { | ||||
|           this.newInvoice.items.forEach((_i) => { | ||||
|             if (_i.taxes && !_i.taxes.length) | ||||
|               _i.taxes.push({ ...taxStub, id: Guid.raw() }) | ||||
|           }) | ||||
|         } | ||||
|  | ||||
|         if (this.newInvoice.discount_per_item === 'YES') { | ||||
|           this.newInvoice.items.forEach((_i, index) => { | ||||
|             if (_i.discount_type === 'fixed') | ||||
|               this.newInvoice.items[index].discount = _i.discount / 100 | ||||
|           }) | ||||
|         } | ||||
|         else { | ||||
|           if (this.newInvoice.discount_type === 'fixed') | ||||
|             this.newInvoice.discount = this.newInvoice.discount / 100 | ||||
|         } | ||||
|       }, | ||||
|  | ||||
|       setCustomerAddresses(customer) { | ||||
|         const customer_business = customer.customer_business | ||||
|  | ||||
|         if (customer_business?.billing_address) | ||||
|           this.newInvoice.customer.billing_address = customer_business.billing_address | ||||
|  | ||||
|         if (customer_business?.shipping_address) | ||||
|           this.newInvoice.customer.shipping_address = customer_business.shipping_address | ||||
|       }, | ||||
|  | ||||
|       addSalesTaxUs() { | ||||
|         const taxTypeStore = useTaxTypeStore() | ||||
|         let salesTax = { ...taxStub } | ||||
|  | ||||
| @ -138,6 +138,7 @@ | ||||
|  | ||||
| <script setup> | ||||
| import { computed, ref, watch, onMounted } from 'vue' | ||||
| import { cloneDeep } from 'lodash' | ||||
| import { useRoute, useRouter } from 'vue-router' | ||||
| import { useI18n } from 'vue-i18n' | ||||
| import { | ||||
| @ -257,11 +258,30 @@ async function submitForm() { | ||||
|  | ||||
|   isSaving.value = true | ||||
|  | ||||
|   let data = { | ||||
|   let data = cloneDeep({ | ||||
|     ...estimateStore.newEstimate, | ||||
|     sub_total: estimateStore.getSubTotal, | ||||
|     total: estimateStore.getTotal, | ||||
|     tax: estimateStore.getTotalTax, | ||||
|   }) | ||||
|   if (data.discount_per_item === 'YES') { | ||||
|     data.items.forEach((item, index) => { | ||||
|       if (item.discount_type === 'fixed'){ | ||||
|         data.items[index].discount = Math.round(item.discount * 100) | ||||
|       } | ||||
|     }) | ||||
|   } | ||||
|   else { | ||||
|     if (data.discount_type === 'fixed'){ | ||||
|       data.discount = Math.round(data.discount * 100) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if ( | ||||
|     !estimateStore.newEstimate.tax_per_item === 'YES' | ||||
|     && data.taxes.length | ||||
|   ){ | ||||
|     data.tax_type_ids = data.taxes.map(_t => _t.tax_type_id) | ||||
|   } | ||||
|  | ||||
|   const action = isEdit.value | ||||
|  | ||||
| @ -147,6 +147,7 @@ import { | ||||
|   decimal, | ||||
| } from '@vuelidate/validators' | ||||
| import useVuelidate from '@vuelidate/core' | ||||
| import { cloneDeep } from 'lodash' | ||||
|  | ||||
| import { useInvoiceStore } from '@/scripts/admin/stores/invoice' | ||||
| import { useModuleStore } from '@/scripts/admin/stores/module' | ||||
| @ -258,11 +259,29 @@ async function submitForm() { | ||||
|  | ||||
|   isSaving.value = true | ||||
|  | ||||
|   let data = { | ||||
|   let data = cloneDeep({ | ||||
|     ...invoiceStore.newInvoice, | ||||
|     sub_total: invoiceStore.getSubTotal, | ||||
|     total: invoiceStore.getTotal, | ||||
|     tax: invoiceStore.getTotalTax, | ||||
|   }) | ||||
|   if (data.discount_per_item === 'YES') { | ||||
|     data.items.forEach((item, index) => { | ||||
|       if (item.discount_type === 'fixed'){ | ||||
|         data.items[index].discount = item.discount * 100 | ||||
|       } | ||||
|     }) | ||||
|   } | ||||
|   else { | ||||
|     if (data.discount_type === 'fixed'){ | ||||
|       data.discount = data.discount * 100 | ||||
|     } | ||||
|   } | ||||
|     if ( | ||||
|     !invoiceStore.newInvoice.tax_per_item === 'YES' | ||||
|     && data.taxes.length | ||||
|   ){ | ||||
|     data.tax_type_ids = data.taxes.map(_t => _t.tax_type_id) | ||||
|   } | ||||
|  | ||||
|   try { | ||||
|  | ||||
| @ -20,21 +20,6 @@ | ||||
|       :data="fetchData" | ||||
|       :columns="taxTypeColumns" | ||||
|     > | ||||
|       <template #cell-compound_tax="{ row }"> | ||||
|         <BaseBadge | ||||
|           :bg-color=" | ||||
|             utils.getBadgeStatusColor(row.data.compound_tax ? 'YES' : 'NO') | ||||
|               .bgColor | ||||
|           " | ||||
|           :color=" | ||||
|             utils.getBadgeStatusColor(row.data.compound_tax ? 'YES' : 'NO') | ||||
|               .color | ||||
|           " | ||||
|         > | ||||
|           {{ row.data.compound_tax ? 'Yes' : 'No'.replace('_', ' ') }} | ||||
|         </BaseBadge> | ||||
|       </template> | ||||
|  | ||||
|       <template #cell-percent="{ row }"> {{ row.data.percent }} % </template> | ||||
|  | ||||
|       <template v-if="hasAtleastOneAbility()" #cell-actions="{ row }"> | ||||
| @ -91,11 +76,6 @@ const taxTypeColumns = computed(() => { | ||||
|       thClass: 'extra', | ||||
|       tdClass: 'font-medium text-gray-900', | ||||
|     }, | ||||
|     { | ||||
|       key: 'compound_tax', | ||||
|       label: t('settings.tax_types.compound_tax'), | ||||
|       tdClass: 'font-medium text-gray-900', | ||||
|     }, | ||||
|     { | ||||
|       key: 'percent', | ||||
|       label: t('settings.tax_types.percent'), | ||||
|  | ||||
| @ -309,6 +309,8 @@ function changeSorting(column) { | ||||
|   } | ||||
|  | ||||
|   if (!usesLocalData.value) { | ||||
|     if (pagination.value) | ||||
|       pagination.value.currentPage = 1 | ||||
|     mapDataToRows() | ||||
|   } | ||||
| } | ||||
| @ -326,7 +328,10 @@ async function pageChange(page) { | ||||
|   await mapDataToRows() | ||||
| } | ||||
|  | ||||
| async function refresh() { | ||||
| async function refresh(isPreservePage = false) { | ||||
|   if (pagination.value && !isPreservePage) | ||||
|     pagination.value.currentPage = 1 | ||||
|  | ||||
|   await mapDataToRows() | ||||
| } | ||||
|  | ||||
|  | ||||
| @ -11,7 +11,8 @@ RUN apt-get update && apt-get install -y \ | ||||
|     unzip \ | ||||
|     libzip-dev \ | ||||
|     libmagickwand-dev \ | ||||
|     mariadb-client | ||||
|     mariadb-client \ | ||||
|     npm | ||||
|  | ||||
| # Clear cache | ||||
| RUN apt-get clean && rm -rf /var/lib/apt/lists/* | ||||
| @ -45,4 +46,19 @@ RUN chmod -R 775 composer.json composer.lock \ | ||||
| RUN chown -R $(whoami):$(whoami) /var/log/ | ||||
| RUN chmod -R 775 /var/log | ||||
|  | ||||
| # Cleanup manually generated build files | ||||
| RUN rm -rf /var/www/public/build | ||||
| RUN npm config set user 0 | ||||
| RUN npm config set unsafe-perm true | ||||
| # Frontend bulding | ||||
| RUN sed -i 's/DB_CONNECTION=mysql/DB_CONNECTION=sqlite/g' /var/www/.env | ||||
| RUN sed -i 's/DB_DATABASE=crater/DB_DATABASE=\/tmp\/crater.sqlite/g' /var/www/.env | ||||
| RUN touch /tmp/crater.sqlite | ||||
| RUN composer install --no-interaction --prefer-dist | ||||
| RUN npm i -f | ||||
| RUN npm install --save-dev sass | ||||
| RUN export NODE_OPTIONS="--max-old-space-size=4096" && /usr/bin/npx vite build --target=es2020 | ||||
| RUN sed -i 's/DB_CONNECTION=sqlite/DB_CONNECTION=mysql/g' /var/www/.env | ||||
| RUN sed -i 's/DB_DATABASE=\/tmp\/crater.sqlite/DB_DATABASE=crater/g' /var/www/.env | ||||
|  | ||||
| USER crater-user | ||||
|  | ||||
| @ -1,7 +1,9 @@ | ||||
| ARG BASE_IMAGE | ||||
|  | ||||
| FROM $BASE_IMAGE as build | ||||
| FROM nginx:1.17-alpine | ||||
|  | ||||
| RUN rm /etc/nginx/conf.d/default.conf | ||||
|  | ||||
| COPY ./ /var/www | ||||
| COPY --from=build /var/www /var/www | ||||
| COPY ./uffizzi/nginx/nginx /etc/nginx/conf.d/ | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	