mirror of
https://github.com/crater-invoice/crater.git
synced 2025-10-28 12:11:08 -04:00
Compare commits
2 Commits
tax-calcul
...
report-pdf
| Author | SHA1 | Date | |
|---|---|---|---|
| 0c821b8ae9 | |||
| bbddd88573 |
161
.github/workflows/uffizzi-build.yml
vendored
161
.github/workflows/uffizzi-build.yml
vendored
@ -1,161 +0,0 @@
|
|||||||
name: Build PR Image
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened,synchronize,reopened,closed]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
|
|
||||||
build-application:
|
|
||||||
name: Build and Push `application`
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.event_name != 'pull_request' || github.event.action != 'closed' }}
|
|
||||||
outputs:
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout git repo
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Generate UUID image name
|
|
||||||
id: uuid
|
|
||||||
run: echo "UUID_TAG_APP=$(uuidgen)" >> $GITHUB_ENV
|
|
||||||
- name: Docker metadata
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v3
|
|
||||||
with:
|
|
||||||
images: registry.uffizzi.com/${{ env.UUID_TAG_APP }}
|
|
||||||
tags: type=raw,value=60d
|
|
||||||
- name: Build and Push Image to registry.uffizzi.com ephemeral registry
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
push: true
|
|
||||||
context: ./
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
file: ./uffizzi/Dockerfile
|
|
||||||
|
|
||||||
build-nginx:
|
|
||||||
needs:
|
|
||||||
- build-application
|
|
||||||
name: Build and Push `nginx`
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.event_name != 'pull_request' || github.event.action != 'closed' }}
|
|
||||||
outputs:
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
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_NGINX=$(uuidgen)" >> $GITHUB_ENV
|
|
||||||
- name: Docker metadata
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v3
|
|
||||||
with:
|
|
||||||
images: registry.uffizzi.com/${{ env.UUID_TAG_NGINX }}
|
|
||||||
tags: type=raw,value=60d
|
|
||||||
- name: Build and Push Image to Uffizzi ephemeral registry
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
push: true
|
|
||||||
context: ./
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
build-crond:
|
|
||||||
name: Build and Push `crond`
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.event_name != 'pull_request' || github.event.action != 'closed' }}
|
|
||||||
outputs:
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
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_CROND=$(uuidgen)" >> $GITHUB_ENV
|
|
||||||
- name: Docker metadata
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v3
|
|
||||||
with:
|
|
||||||
images: registry.uffizzi.com/${{ env.UUID_TAG_CROND }}
|
|
||||||
tags: type=raw,value=60d
|
|
||||||
- name: Build and Push Image to registry.uffizzi.com ephemeral registry
|
|
||||||
uses: docker/build-push-action@v2
|
|
||||||
with:
|
|
||||||
push: true
|
|
||||||
context: ./
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
file: ./uffizzi/crond/Dockerfile
|
|
||||||
cache-from: type=gha
|
|
||||||
cache-to: type=gha,mode=max
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
render-compose-file:
|
|
||||||
name: Render Docker Compose File
|
|
||||||
# Pass output of this workflow to another triggered by `workflow_run` event.
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
compose-file-cache-key: ${{ steps.hash.outputs.hash }}
|
|
||||||
needs:
|
|
||||||
- build-application
|
|
||||||
- build-nginx
|
|
||||||
- build-crond
|
|
||||||
steps:
|
|
||||||
- name: Checkout git repo
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Render Compose File
|
|
||||||
run: |
|
|
||||||
APP_IMAGE=$(echo ${{ needs.build-application.outputs.tags }})
|
|
||||||
export APP_IMAGE
|
|
||||||
NGINX_IMAGE=$(echo ${{ needs.build-nginx.outputs.tags }})
|
|
||||||
export NGINX_IMAGE
|
|
||||||
CROND_IMAGE=$(echo ${{ needs.build-crond.outputs.tags }})
|
|
||||||
export CROND_IMAGE
|
|
||||||
# Render simple template from environment variables.
|
|
||||||
envsubst < ./uffizzi/docker-compose.uffizzi.yml > docker-compose.rendered.yml
|
|
||||||
cat docker-compose.rendered.yml
|
|
||||||
- name: Upload Rendered Compose File as Artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: preview-spec
|
|
||||||
path: docker-compose.rendered.yml
|
|
||||||
retention-days: 2
|
|
||||||
- name: Serialize PR Event to File
|
|
||||||
run: |
|
|
||||||
cat << EOF > event.json
|
|
||||||
${{ toJSON(github.event) }}
|
|
||||||
|
|
||||||
EOF
|
|
||||||
- name: Upload PR Event as Artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: preview-spec
|
|
||||||
path: event.json
|
|
||||||
retention-days: 2
|
|
||||||
|
|
||||||
delete-preview:
|
|
||||||
name: Call for Preview Deletion
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: ${{ github.event.action == 'closed' }}
|
|
||||||
steps:
|
|
||||||
# If this PR is closing, we will not render a compose file nor pass it to the next workflow.
|
|
||||||
- name: Serialize PR Event to File
|
|
||||||
run: echo '${{ toJSON(github.event) }}' > event.json
|
|
||||||
- name: Upload PR Event as Artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: preview-spec
|
|
||||||
path: event.json
|
|
||||||
retention-days: 2
|
|
||||||
|
|
||||||
84
.github/workflows/uffizzi-preview.yml
vendored
84
.github/workflows/uffizzi-preview.yml
vendored
@ -1,84 +0,0 @@
|
|||||||
name: Deploy Uffizzi Preview
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_run:
|
|
||||||
workflows:
|
|
||||||
- "Build PR Image"
|
|
||||||
types:
|
|
||||||
- completed
|
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
cache-compose-file:
|
|
||||||
name: Cache Compose File
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
compose-file-cache-key: ${{ env.COMPOSE_FILE_HASH }}
|
|
||||||
pr-number: ${{ env.PR_NUMBER }}
|
|
||||||
steps:
|
|
||||||
- name: 'Download artifacts'
|
|
||||||
# Fetch output (zip archive) from the workflow run that triggered this workflow.
|
|
||||||
uses: actions/github-script@v6
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
run_id: context.payload.workflow_run.id,
|
|
||||||
});
|
|
||||||
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
|
|
||||||
return artifact.name == "preview-spec"
|
|
||||||
})[0];
|
|
||||||
let download = await github.rest.actions.downloadArtifact({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
artifact_id: matchArtifact.id,
|
|
||||||
archive_format: 'zip',
|
|
||||||
});
|
|
||||||
let fs = require('fs');
|
|
||||||
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/preview-spec.zip`, Buffer.from(download.data));
|
|
||||||
- name: 'Unzip artifact'
|
|
||||||
run: unzip preview-spec.zip
|
|
||||||
- name: Read Event into ENV
|
|
||||||
run: |
|
|
||||||
echo 'EVENT_JSON<<EOF' >> $GITHUB_ENV
|
|
||||||
cat event.json >> $GITHUB_ENV
|
|
||||||
echo 'EOF' >> $GITHUB_ENV
|
|
||||||
- name: Hash Rendered Compose File
|
|
||||||
id: hash
|
|
||||||
# If the previous workflow was triggered by a PR close event, we will not have a compose file artifact.
|
|
||||||
if: ${{ fromJSON(env.EVENT_JSON).action != 'closed' }}
|
|
||||||
run: echo "COMPOSE_FILE_HASH=$(md5sum docker-compose.rendered.yml | awk '{ print $1 }')" >> $GITHUB_ENV
|
|
||||||
- name: Cache Rendered Compose File
|
|
||||||
if: ${{ fromJSON(env.EVENT_JSON).action != 'closed' }}
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: docker-compose.rendered.yml
|
|
||||||
key: ${{ env.COMPOSE_FILE_HASH }}
|
|
||||||
|
|
||||||
- name: Read PR Number From Event Object
|
|
||||||
id: pr
|
|
||||||
run: echo "PR_NUMBER=${{ fromJSON(env.EVENT_JSON).number }}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: DEBUG - Print Job Outputs
|
|
||||||
if: ${{ runner.debug }}
|
|
||||||
run: |
|
|
||||||
echo "PR number: ${{ env.PR_NUMBER }}"
|
|
||||||
echo "Compose file hash: ${{ env.COMPOSE_FILE_HASH }}"
|
|
||||||
cat event.json
|
|
||||||
deploy-uffizzi-preview:
|
|
||||||
name: Use Remote Workflow to Preview on Uffizzi
|
|
||||||
needs:
|
|
||||||
- cache-compose-file
|
|
||||||
uses: UffizziCloud/preview-action/.github/workflows/reusable.yaml@v2.6.1
|
|
||||||
with:
|
|
||||||
# If this workflow was triggered by a PR close event, cache-key will be an empty string
|
|
||||||
# and this reusable workflow will delete the preview deployment.
|
|
||||||
compose-file-cache-key: ${{ needs.cache-compose-file.outputs.compose-file-cache-key }}
|
|
||||||
compose-file-cache-path: docker-compose.rendered.yml
|
|
||||||
server: https://app.uffizzi.com/
|
|
||||||
pr-number: ${{ needs.cache-compose-file.outputs.pr-number }}
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: write
|
|
||||||
id-token: write
|
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -16,5 +16,4 @@ Homestead.yaml
|
|||||||
.gitkeep
|
.gitkeep
|
||||||
/public/docs
|
/public/docs
|
||||||
/.scribe
|
/.scribe
|
||||||
!storage/fonts/.gitkeep
|
!storage/fonts/.gitkeep
|
||||||
.DS_Store
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
FROM php:8.1-fpm
|
FROM php:7.4-fpm
|
||||||
|
|
||||||
# Arguments defined in docker-compose.yml
|
# Arguments defined in docker-compose.yml
|
||||||
ARG user
|
ARG user
|
||||||
|
|||||||
@ -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(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"));
|
copy(resource_path("/static/img/PDF/{$type}1.png"), resource_path("/static/img/PDF/{$templateName}.png"));
|
||||||
|
|
||||||
$path = resource_path("views/app/pdf/{$type}/{$templateName}.blade.php");
|
$path = resource_path("app/pdf/{$type}/{$templateName}.blade.php");
|
||||||
$type = ucfirst($type);
|
$type = ucfirst($type);
|
||||||
$this->info("{$type} Template created successfully at ".$path);
|
$this->info("{$type} Template created successfully at ".$path);
|
||||||
|
|
||||||
|
|||||||
@ -45,21 +45,15 @@ class EstimatesRequest extends FormRequest
|
|||||||
'nullable'
|
'nullable'
|
||||||
],
|
],
|
||||||
'discount' => [
|
'discount' => [
|
||||||
'numeric',
|
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'discount_val' => [
|
'discount_val' => [
|
||||||
'integer',
|
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'sub_total' => [
|
'sub_total' => [
|
||||||
'integer',
|
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'total' => [
|
'total' => [
|
||||||
'integer',
|
|
||||||
'numeric',
|
|
||||||
'max:99999999',
|
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'tax' => [
|
'tax' => [
|
||||||
@ -83,11 +77,9 @@ class EstimatesRequest extends FormRequest
|
|||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'items.*.quantity' => [
|
'items.*.quantity' => [
|
||||||
'integer',
|
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'items.*.price' => [
|
'items.*.price' => [
|
||||||
'integer',
|
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
@ -45,21 +45,15 @@ class InvoicesRequest extends FormRequest
|
|||||||
'nullable'
|
'nullable'
|
||||||
],
|
],
|
||||||
'discount' => [
|
'discount' => [
|
||||||
'numeric',
|
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'discount_val' => [
|
'discount_val' => [
|
||||||
'integer',
|
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'sub_total' => [
|
'sub_total' => [
|
||||||
'integer',
|
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'total' => [
|
'total' => [
|
||||||
'integer',
|
|
||||||
'numeric',
|
|
||||||
'max:99999999',
|
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'tax' => [
|
'tax' => [
|
||||||
@ -83,11 +77,9 @@ class InvoicesRequest extends FormRequest
|
|||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'items.*.quantity' => [
|
'items.*.quantity' => [
|
||||||
'integer',
|
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'items.*.price' => [
|
'items.*.price' => [
|
||||||
'integer',
|
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
@ -43,21 +43,15 @@ class RecurringInvoiceRequest extends FormRequest
|
|||||||
'nullable'
|
'nullable'
|
||||||
],
|
],
|
||||||
'discount' => [
|
'discount' => [
|
||||||
'numeric',
|
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'discount_val' => [
|
'discount_val' => [
|
||||||
'integer',
|
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'sub_total' => [
|
'sub_total' => [
|
||||||
'integer',
|
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'total' => [
|
'total' => [
|
||||||
'integer',
|
|
||||||
'numeric',
|
|
||||||
'max:99999999',
|
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'tax' => [
|
'tax' => [
|
||||||
|
|||||||
@ -38,7 +38,7 @@
|
|||||||
"barryvdh/laravel-ide-helper": "^2.6",
|
"barryvdh/laravel-ide-helper": "^2.6",
|
||||||
"beyondcode/laravel-dump-server": "^1.0",
|
"beyondcode/laravel-dump-server": "^1.0",
|
||||||
"facade/ignition": "^2.3.6",
|
"facade/ignition": "^2.3.6",
|
||||||
"friendsofphp/php-cs-fixer": "^3.8",
|
"friendsofphp/php-cs-fixer": "^3.0",
|
||||||
"fakerphp/faker": "^1.9.1",
|
"fakerphp/faker": "^1.9.1",
|
||||||
"mockery/mockery": "^1.3.1",
|
"mockery/mockery": "^1.3.1",
|
||||||
"nunomaduro/collision": "^5.0",
|
"nunomaduro/collision": "^5.0",
|
||||||
@ -81,10 +81,7 @@
|
|||||||
"config": {
|
"config": {
|
||||||
"optimize-autoloader": true,
|
"optimize-autoloader": true,
|
||||||
"preferred-install": "dist",
|
"preferred-install": "dist",
|
||||||
"sort-packages": true,
|
"sort-packages": true
|
||||||
"allow-plugins": {
|
|
||||||
"pestphp/pest-plugin": true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"laravel": {
|
"laravel": {
|
||||||
|
|||||||
2345
composer.lock
generated
2345
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -27,7 +27,6 @@ return [
|
|||||||
'tokenizer',
|
'tokenizer',
|
||||||
'JSON',
|
'JSON',
|
||||||
'cURL',
|
'cURL',
|
||||||
'zip',
|
|
||||||
],
|
],
|
||||||
'apache' => [
|
'apache' => [
|
||||||
'mod_rewrite',
|
'mod_rewrite',
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
|
docker compose exec app composer config --no-plugins allow-plugins.pestphp/pest-plugin true
|
||||||
docker-compose exec app composer install --no-interaction --prefer-dist --optimize-autoloader
|
docker-compose exec app composer install --no-interaction --prefer-dist --optimize-autoloader
|
||||||
|
|
||||||
docker-compose exec app php artisan storage:link || true
|
docker-compose exec app php artisan storage:link || true
|
||||||
|
|||||||
@ -17,7 +17,18 @@
|
|||||||
<td class="px-5 py-4 text-left align-top">
|
<td class="px-5 py-4 text-left align-top">
|
||||||
<div class="flex justify-start">
|
<div class="flex justify-start">
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-center w-5 h-5 mt-2 mr-2 text-gray-300 cursor-move handle"
|
class="
|
||||||
|
flex
|
||||||
|
items-center
|
||||||
|
justify-center
|
||||||
|
w-5
|
||||||
|
h-5
|
||||||
|
mt-2
|
||||||
|
text-gray-300
|
||||||
|
cursor-move
|
||||||
|
handle
|
||||||
|
mr-2
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<DragIcon />
|
<DragIcon />
|
||||||
</div>
|
</div>
|
||||||
@ -97,7 +108,7 @@
|
|||||||
|
|
||||||
<BaseIcon
|
<BaseIcon
|
||||||
name="ChevronDownIcon"
|
name="ChevronDownIcon"
|
||||||
class="w-4 h-4 ml-1 text-gray-500"
|
class="w-4 h-4 text-gray-500 ml-1"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
@ -144,7 +155,7 @@
|
|||||||
<BaseContentPlaceholders v-if="loading">
|
<BaseContentPlaceholders v-if="loading">
|
||||||
<BaseContentPlaceholdersText
|
<BaseContentPlaceholdersText
|
||||||
:lines="1"
|
:lines="1"
|
||||||
class="w-24 h-8 border rounded-md"
|
class="w-24 h-8 rounded-md border"
|
||||||
/>
|
/>
|
||||||
</BaseContentPlaceholders>
|
</BaseContentPlaceholders>
|
||||||
|
|
||||||
@ -164,7 +175,6 @@
|
|||||||
:ability="abilities.CREATE_INVOICE"
|
:ability="abilities.CREATE_INVOICE"
|
||||||
:store="store"
|
:store="store"
|
||||||
:store-prop="storeProp"
|
:store-prop="storeProp"
|
||||||
:discount="discount"
|
|
||||||
@update="updateTax"
|
@update="updateTax"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
@ -271,19 +281,23 @@ const price = computed({
|
|||||||
} else {
|
} else {
|
||||||
updateItemAttribute('price', newValue)
|
updateItemAttribute('price', newValue)
|
||||||
}
|
}
|
||||||
setDiscount()
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const subtotal = computed(() => Math.round(props.itemData.price * props.itemData.quantity))
|
const subtotal = computed(() => props.itemData.price * props.itemData.quantity)
|
||||||
|
|
||||||
const discount = computed({
|
const discount = computed({
|
||||||
get: () => {
|
get: () => {
|
||||||
return props.itemData.discount
|
return props.itemData.discount
|
||||||
},
|
},
|
||||||
set: (newValue) => {
|
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)
|
updateItemAttribute('discount', newValue)
|
||||||
setDiscount()
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -309,7 +323,7 @@ const showRemoveButton = computed(() => {
|
|||||||
const totalSimpleTax = computed(() => {
|
const totalSimpleTax = computed(() => {
|
||||||
return Math.round(
|
return Math.round(
|
||||||
sumBy(props.itemData.taxes, function (tax) {
|
sumBy(props.itemData.taxes, function (tax) {
|
||||||
if (tax.amount) {
|
if (!tax.compound_tax) {
|
||||||
return tax.amount
|
return tax.amount
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
@ -317,7 +331,18 @@ const totalSimpleTax = computed(() => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const totalTax = computed(() => totalSimpleTax.value)
|
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 rules = {
|
const rules = {
|
||||||
name: {
|
name: {
|
||||||
@ -384,7 +409,7 @@ const v$ = useVuelidate(
|
|||||||
|
|
||||||
function updateTax(data) {
|
function updateTax(data) {
|
||||||
props.store.$patch((state) => {
|
props.store.$patch((state) => {
|
||||||
state[props.storeProp].items[props.index]['taxes'][data.index] = data.item
|
state[props.storeProp].items[props.index]['taxes'][data.index] = data.item
|
||||||
})
|
})
|
||||||
|
|
||||||
let lastTax = props.itemData.taxes[props.itemData.taxes.length - 1]
|
let lastTax = props.itemData.taxes[props.itemData.taxes.length - 1]
|
||||||
@ -401,16 +426,6 @@ function updateTax(data) {
|
|||||||
syncItemToStore()
|
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) {
|
function searchVal(val) {
|
||||||
updateItemAttribute('name', val)
|
updateItemAttribute('name', val)
|
||||||
}
|
}
|
||||||
@ -480,12 +495,10 @@ function syncItemToStore() {
|
|||||||
total: total.value,
|
total: total.value,
|
||||||
sub_total: subtotal.value,
|
sub_total: subtotal.value,
|
||||||
totalSimpleTax: totalSimpleTax.value,
|
totalSimpleTax: totalSimpleTax.value,
|
||||||
|
totalCompoundTax: totalCompoundTax.value,
|
||||||
totalTax: totalTax.value,
|
totalTax: totalTax.value,
|
||||||
tax: totalTax.value,
|
tax: totalTax.value,
|
||||||
taxes: [...itemTaxes],
|
taxes: [...itemTaxes],
|
||||||
tax_type_ids: itemTaxes.flatMap(_t =>
|
|
||||||
_t.tax_type_id ? _t.tax_type_id : [],
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
props.store.updateItem(data)
|
props.store.updateItem(data)
|
||||||
|
|||||||
@ -30,13 +30,24 @@
|
|||||||
<template v-if="userStore.hasAbilities(ability)" #action>
|
<template v-if="userStore.hasAbilities(ability)" #action>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="flex items-center justify-center w-full px-2 py-2 bg-gray-200 border-none outline-none cursor-pointer "
|
class="
|
||||||
|
flex
|
||||||
|
items-center
|
||||||
|
justify-center
|
||||||
|
w-full
|
||||||
|
px-2
|
||||||
|
cursor-pointer
|
||||||
|
py-2
|
||||||
|
bg-gray-200
|
||||||
|
border-none
|
||||||
|
outline-none
|
||||||
|
"
|
||||||
@click="openTaxModal"
|
@click="openTaxModal"
|
||||||
>
|
>
|
||||||
<BaseIcon name="CheckCircleIcon" class="h-5 text-primary-400" />
|
<BaseIcon name="CheckCircleIcon" class="h-5 text-primary-400" />
|
||||||
|
|
||||||
<label
|
<label
|
||||||
class="ml-2 text-sm leading-none cursor-pointer text-primary-400"
|
class="ml-2 text-sm leading-none text-primary-400 cursor-pointer"
|
||||||
>{{ $t('invoices.add_new_tax') }}</label
|
>{{ $t('invoices.add_new_tax') }}</label
|
||||||
>
|
>
|
||||||
</button>
|
</button>
|
||||||
@ -104,10 +115,6 @@ const props = defineProps({
|
|||||||
type: Number,
|
type: Number,
|
||||||
default: 0,
|
default: 0,
|
||||||
},
|
},
|
||||||
discountedTotal: {
|
|
||||||
type: Number,
|
|
||||||
default: 0,
|
|
||||||
},
|
|
||||||
currency: {
|
currency: {
|
||||||
type: [Object, String],
|
type: [Object, String],
|
||||||
required: true,
|
required: true,
|
||||||
@ -146,19 +153,19 @@ const filteredTypes = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const taxAmount = computed(() => {
|
const taxAmount = computed(() => {
|
||||||
if (props.discountedTotal && localTax.percent) {
|
if (localTax.compound_tax && props.total) {
|
||||||
const taxPerItemEnabled = props.store[props.storeProp].tax_per_item === 'YES'
|
return ((props.total + props.totalTax) * localTax.percent) / 100
|
||||||
const discountPerItemEnabled = props.store[props.storeProp].discount_per_item === 'YES'
|
|
||||||
if (taxPerItemEnabled && !discountPerItemEnabled){
|
|
||||||
return getTaxAmount()
|
|
||||||
}
|
|
||||||
return (props.discountedTotal * localTax.percent) / 100
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (props.total && localTax.percent) {
|
||||||
|
return (props.total * localTax.percent) / 100
|
||||||
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.discountedTotal,
|
() => props.total,
|
||||||
() => {
|
() => {
|
||||||
updateRowTax()
|
updateRowTax()
|
||||||
}
|
}
|
||||||
@ -171,13 +178,6 @@ watch(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(
|
|
||||||
() => taxAmount.value,
|
|
||||||
() => {
|
|
||||||
updateRowTax()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
// Set SelectedTax
|
// Set SelectedTax
|
||||||
if (props.taxData.tax_type_id > 0) {
|
if (props.taxData.tax_type_id > 0) {
|
||||||
selectedTax.value = taxTypeStore.taxTypes.find(
|
selectedTax.value = taxTypeStore.taxTypes.find(
|
||||||
@ -190,6 +190,7 @@ updateRowTax()
|
|||||||
function onSelectTax(val) {
|
function onSelectTax(val) {
|
||||||
localTax.percent = val.percent
|
localTax.percent = val.percent
|
||||||
localTax.tax_type_id = val.id
|
localTax.tax_type_id = val.id
|
||||||
|
localTax.compound_tax = val.compound_tax
|
||||||
localTax.name = val.name
|
localTax.name = val.name
|
||||||
|
|
||||||
updateRowTax()
|
updateRowTax()
|
||||||
@ -226,27 +227,6 @@ function openTaxModal() {
|
|||||||
function removeTax(index) {
|
function removeTax(index) {
|
||||||
props.store.$patch((state) => {
|
props.store.$patch((state) => {
|
||||||
state[props.storeProp].items[props.itemIndex].taxes.splice(index, 1)
|
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>
|
</script>
|
||||||
|
|||||||
@ -29,7 +29,14 @@
|
|||||||
|
|
||||||
<label
|
<label
|
||||||
v-else
|
v-else
|
||||||
class="flex items-center justify-center m-0 text-lg text-black uppercase "
|
class="
|
||||||
|
flex
|
||||||
|
items-center
|
||||||
|
justify-center
|
||||||
|
m-0
|
||||||
|
text-lg text-black
|
||||||
|
uppercase
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<BaseFormatMoney
|
<BaseFormatMoney
|
||||||
:amount="store.getSubTotal"
|
:amount="store.getSubTotal"
|
||||||
@ -59,7 +66,14 @@
|
|||||||
|
|
||||||
<label
|
<label
|
||||||
v-else-if="store[storeProp].tax_per_item === 'YES'"
|
v-else-if="store[storeProp].tax_per_item === 'YES'"
|
||||||
class="flex items-center justify-center m-0 text-lg text-black uppercase "
|
class="
|
||||||
|
flex
|
||||||
|
items-center
|
||||||
|
justify-center
|
||||||
|
m-0
|
||||||
|
text-lg text-black
|
||||||
|
uppercase
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<BaseFormatMoney :amount="tax.amount" :currency="defaultCurrency" />
|
<BaseFormatMoney :amount="tax.amount" :currency="defaultCurrency" />
|
||||||
</label>
|
</label>
|
||||||
@ -84,7 +98,7 @@
|
|||||||
<BaseContentPlaceholders v-if="isLoading">
|
<BaseContentPlaceholders v-if="isLoading">
|
||||||
<BaseContentPlaceholdersText
|
<BaseContentPlaceholdersText
|
||||||
:lines="1"
|
:lines="1"
|
||||||
class="w-24 h-8 border rounded-md"
|
class="w-24 h-8 rounded-md border"
|
||||||
/>
|
/>
|
||||||
</BaseContentPlaceholders>
|
</BaseContentPlaceholders>
|
||||||
<div v-else class="flex" style="width: 140px" role="group">
|
<div v-else class="flex" style="width: 140px" role="group">
|
||||||
@ -100,7 +114,7 @@
|
|||||||
<BaseDropdown position="bottom-end">
|
<BaseDropdown position="bottom-end">
|
||||||
<template #activator>
|
<template #activator>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
class="p-2 rounded-none rounded-tr-md rounded-br-md"
|
class="rounded-tr-md rounded-br-md p-2 rounded-none"
|
||||||
type="button"
|
type="button"
|
||||||
variant="white"
|
variant="white"
|
||||||
>
|
>
|
||||||
@ -113,7 +127,7 @@
|
|||||||
|
|
||||||
<BaseIcon
|
<BaseIcon
|
||||||
name="ChevronDownIcon"
|
name="ChevronDownIcon"
|
||||||
class="w-4 h-4 ml-1 text-gray-500"
|
class="w-4 h-4 text-gray-500 ml-1"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
@ -166,7 +180,15 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="flex items-center justify-between w-full pt-2 mt-5 border-t border-gray-200 border-solid "
|
class="
|
||||||
|
flex
|
||||||
|
items-center
|
||||||
|
justify-between
|
||||||
|
w-full
|
||||||
|
pt-2
|
||||||
|
mt-5
|
||||||
|
border-t border-gray-200 border-solid
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<BaseContentPlaceholders v-if="isLoading">
|
<BaseContentPlaceholders v-if="isLoading">
|
||||||
<BaseContentPlaceholdersText :lines="1" class="w-16 h-5" />
|
<BaseContentPlaceholdersText :lines="1" class="w-16 h-5" />
|
||||||
@ -182,7 +204,14 @@
|
|||||||
</BaseContentPlaceholders>
|
</BaseContentPlaceholders>
|
||||||
<label
|
<label
|
||||||
v-else
|
v-else
|
||||||
class="flex items-center justify-center text-lg uppercase text-primary-400"
|
class="
|
||||||
|
flex
|
||||||
|
items-center
|
||||||
|
justify-center
|
||||||
|
text-lg
|
||||||
|
uppercase
|
||||||
|
text-primary-400
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<BaseFormatMoney :amount="store.getTotal" :currency="defaultCurrency" />
|
<BaseFormatMoney :amount="store.getTotal" :currency="defaultCurrency" />
|
||||||
</label>
|
</label>
|
||||||
@ -191,7 +220,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, inject, ref, watch } from 'vue'
|
import { computed, inject, ref } from 'vue'
|
||||||
import Guid from 'guid'
|
import Guid from 'guid'
|
||||||
import Tax from './CreateTotalTaxes.vue'
|
import Tax from './CreateTotalTaxes.vue'
|
||||||
import TaxStub from '@/scripts/admin/stub/abilities'
|
import TaxStub from '@/scripts/admin/stub/abilities'
|
||||||
@ -227,20 +256,19 @@ const utils = inject('$utils')
|
|||||||
|
|
||||||
const companyStore = useCompanyStore()
|
const companyStore = useCompanyStore()
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.store[props.storeProp].items,
|
|
||||||
(val) => {
|
|
||||||
setDiscount()
|
|
||||||
}, { deep: true },
|
|
||||||
)
|
|
||||||
|
|
||||||
const totalDiscount = computed({
|
const totalDiscount = computed({
|
||||||
get: () => {
|
get: () => {
|
||||||
return props.store[props.storeProp].discount
|
return props.store[props.storeProp].discount
|
||||||
},
|
},
|
||||||
set: (newValue) => {
|
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
|
props.store[props.storeProp].discount = newValue
|
||||||
setDiscount()
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -266,7 +294,7 @@ const itemWiseTaxes = computed(() => {
|
|||||||
} else if (tax.tax_type_id) {
|
} else if (tax.tax_type_id) {
|
||||||
taxes.push({
|
taxes.push({
|
||||||
tax_type_id: tax.tax_type_id,
|
tax_type_id: tax.tax_type_id,
|
||||||
amount: Math.round(tax.amount),
|
amount: tax.amount,
|
||||||
percent: tax.percent,
|
percent: tax.percent,
|
||||||
name: tax.name,
|
name: tax.name,
|
||||||
})
|
})
|
||||||
@ -285,19 +313,6 @@ 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() {
|
function selectFixed() {
|
||||||
if (props.store[props.storeProp].discount_type === 'fixed') {
|
if (props.store[props.storeProp].discount_type === 'fixed') {
|
||||||
return
|
return
|
||||||
@ -309,21 +324,23 @@ function selectFixed() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function selectPercentage() {
|
function selectPercentage() {
|
||||||
if (props.store[props.storeProp].discount_type === 'percentage'){
|
if (props.store[props.storeProp].discount_type === 'percentage') {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
props.store[props.storeProp].discount_val =
|
||||||
const val = Math.round(props.store[props.storeProp].discount * 100) / 100
|
(props.store.getSubTotal * props.store[props.storeProp].discount) / 100
|
||||||
|
|
||||||
props.store[props.storeProp].discount_val
|
|
||||||
= Math.round((props.store.getSubTotal * val) / 100)
|
|
||||||
|
|
||||||
props.store[props.storeProp].discount_type = 'percentage'
|
props.store[props.storeProp].discount_type = 'percentage'
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSelectTax(selectedTax) {
|
function onSelectTax(selectedTax) {
|
||||||
let amount = 0
|
let amount = 0
|
||||||
if (props.store.getSubtotalWithDiscount && selectedTax.percent) {
|
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) {
|
||||||
amount = Math.round(
|
amount = Math.round(
|
||||||
(props.store.getSubtotalWithDiscount * selectedTax.percent) / 100
|
(props.store.getSubtotalWithDiscount * selectedTax.percent) / 100
|
||||||
)
|
)
|
||||||
@ -334,6 +351,7 @@ function onSelectTax(selectedTax) {
|
|||||||
id: Guid.raw(),
|
id: Guid.raw(),
|
||||||
name: selectedTax.name,
|
name: selectedTax.name,
|
||||||
percent: selectedTax.percent,
|
percent: selectedTax.percent,
|
||||||
|
compound_tax: selectedTax.compound_tax,
|
||||||
tax_type_id: selectedTax.id,
|
tax_type_id: selectedTax.id,
|
||||||
amount,
|
amount,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -77,6 +77,17 @@
|
|||||||
@input="v$.currentTaxType.description.$touch()"
|
@input="v$.currentTaxType.description.$touch()"
|
||||||
/>
|
/>
|
||||||
</BaseInputGroup>
|
</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>
|
</BaseInputGrid>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
@ -198,7 +209,14 @@ async function submitTaxTypeData() {
|
|||||||
|
|
||||||
function SelectTax(taxData) {
|
function SelectTax(taxData) {
|
||||||
let amount = 0
|
let amount = 0
|
||||||
if (estimateStore.getSubtotalWithDiscount && taxData.percent) {
|
if (taxData.compound_tax && estimateStore.getSubtotalWithDiscount) {
|
||||||
|
amount = Math.round(
|
||||||
|
((estimateStore.getSubtotalWithDiscount +
|
||||||
|
estimateStore.getTotalSimpleTax) *
|
||||||
|
taxData.percent) /
|
||||||
|
100
|
||||||
|
)
|
||||||
|
} else if (estimateStore.getSubtotalWithDiscount && taxData.percent) {
|
||||||
amount = Math.round(
|
amount = Math.round(
|
||||||
(estimateStore.getSubtotalWithDiscount * taxData.percent) / 100
|
(estimateStore.getSubtotalWithDiscount * taxData.percent) / 100
|
||||||
)
|
)
|
||||||
@ -208,6 +226,7 @@ function SelectTax(taxData) {
|
|||||||
id: Guid.raw(),
|
id: Guid.raw(),
|
||||||
name: taxData.name,
|
name: taxData.name,
|
||||||
percent: taxData.percent,
|
percent: taxData.percent,
|
||||||
|
compound_tax: taxData.compound_tax,
|
||||||
tax_type_id: taxData.id,
|
tax_type_id: taxData.id,
|
||||||
amount,
|
amount,
|
||||||
}
|
}
|
||||||
@ -223,6 +242,7 @@ function selectItemTax(taxData) {
|
|||||||
id: Guid.raw(),
|
id: Guid.raw(),
|
||||||
name: taxData.name,
|
name: taxData.name,
|
||||||
percent: taxData.percent,
|
percent: taxData.percent,
|
||||||
|
compound_tax: taxData.compound_tax,
|
||||||
tax_type_id: taxData.id,
|
tax_type_id: taxData.id,
|
||||||
}
|
}
|
||||||
modalStore.refreshData(data)
|
modalStore.refreshData(data)
|
||||||
|
|||||||
@ -143,7 +143,7 @@
|
|||||||
<template #activator>
|
<template #activator>
|
||||||
<img
|
<img
|
||||||
:src="previewAvatar"
|
:src="previewAvatar"
|
||||||
class="block w-8 h-8 rounded md:h-9 md:w-9 object-cover"
|
class="block w-8 h-8 rounded md:h-9 md:w-9"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@ -143,8 +143,7 @@ export const useEstimateStore = (useWindow = false) => {
|
|||||||
axios
|
axios
|
||||||
.get(`/api/v1/estimates/${id}`)
|
.get(`/api/v1/estimates/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.setEstimateData(response.data.data)
|
Object.assign(this.newEstimate, response.data.data)
|
||||||
this.setCustomerAddresses(this.newEstimate.customer)
|
|
||||||
resolve(response)
|
resolve(response)
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
@ -155,41 +154,6 @@ 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() {
|
addSalesTaxUs() {
|
||||||
const taxTypeStore = useTaxTypeStore()
|
const taxTypeStore = useTaxTypeStore()
|
||||||
let salesTax = { ...taxStub }
|
let salesTax = { ...taxStub }
|
||||||
|
|||||||
@ -133,8 +133,8 @@ export const useInvoiceStore = (useWindow = false) => {
|
|||||||
axios
|
axios
|
||||||
.get(`/api/v1/invoices/${id}`)
|
.get(`/api/v1/invoices/${id}`)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.setInvoiceData(response.data.data)
|
Object.assign(this.newInvoice, response.data.data)
|
||||||
this.setCustomerAddresses(this.newInvoice.customer)
|
this.newInvoice.customer = response.data.data.customer
|
||||||
resolve(response)
|
resolve(response)
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
@ -144,38 +144,6 @@ 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() {
|
addSalesTaxUs() {
|
||||||
const taxTypeStore = useTaxTypeStore()
|
const taxTypeStore = useTaxTypeStore()
|
||||||
let salesTax = { ...taxStub }
|
let salesTax = { ...taxStub }
|
||||||
|
|||||||
@ -138,7 +138,6 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref, watch, onMounted } from 'vue'
|
import { computed, ref, watch, onMounted } from 'vue'
|
||||||
import { cloneDeep } from 'lodash'
|
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import {
|
import {
|
||||||
@ -258,30 +257,11 @@ async function submitForm() {
|
|||||||
|
|
||||||
isSaving.value = true
|
isSaving.value = true
|
||||||
|
|
||||||
let data = cloneDeep({
|
let data = {
|
||||||
...estimateStore.newEstimate,
|
...estimateStore.newEstimate,
|
||||||
sub_total: estimateStore.getSubTotal,
|
sub_total: estimateStore.getSubTotal,
|
||||||
total: estimateStore.getTotal,
|
total: estimateStore.getTotal,
|
||||||
tax: estimateStore.getTotalTax,
|
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
|
const action = isEdit.value
|
||||||
|
|||||||
@ -147,7 +147,6 @@ import {
|
|||||||
decimal,
|
decimal,
|
||||||
} from '@vuelidate/validators'
|
} from '@vuelidate/validators'
|
||||||
import useVuelidate from '@vuelidate/core'
|
import useVuelidate from '@vuelidate/core'
|
||||||
import { cloneDeep } from 'lodash'
|
|
||||||
|
|
||||||
import { useInvoiceStore } from '@/scripts/admin/stores/invoice'
|
import { useInvoiceStore } from '@/scripts/admin/stores/invoice'
|
||||||
import { useModuleStore } from '@/scripts/admin/stores/module'
|
import { useModuleStore } from '@/scripts/admin/stores/module'
|
||||||
@ -259,29 +258,11 @@ async function submitForm() {
|
|||||||
|
|
||||||
isSaving.value = true
|
isSaving.value = true
|
||||||
|
|
||||||
let data = cloneDeep({
|
let data = {
|
||||||
...invoiceStore.newInvoice,
|
...invoiceStore.newInvoice,
|
||||||
sub_total: invoiceStore.getSubTotal,
|
sub_total: invoiceStore.getSubTotal,
|
||||||
total: invoiceStore.getTotal,
|
total: invoiceStore.getTotal,
|
||||||
tax: invoiceStore.getTotalTax,
|
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 {
|
try {
|
||||||
|
|||||||
@ -20,6 +20,21 @@
|
|||||||
:data="fetchData"
|
:data="fetchData"
|
||||||
:columns="taxTypeColumns"
|
: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 #cell-percent="{ row }"> {{ row.data.percent }} % </template>
|
||||||
|
|
||||||
<template v-if="hasAtleastOneAbility()" #cell-actions="{ row }">
|
<template v-if="hasAtleastOneAbility()" #cell-actions="{ row }">
|
||||||
@ -76,6 +91,11 @@ const taxTypeColumns = computed(() => {
|
|||||||
thClass: 'extra',
|
thClass: 'extra',
|
||||||
tdClass: 'font-medium text-gray-900',
|
tdClass: 'font-medium text-gray-900',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'compound_tax',
|
||||||
|
label: t('settings.tax_types.compound_tax'),
|
||||||
|
tdClass: 'font-medium text-gray-900',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'percent',
|
key: 'percent',
|
||||||
label: t('settings.tax_types.percent'),
|
label: t('settings.tax_types.percent'),
|
||||||
|
|||||||
@ -309,8 +309,6 @@ function changeSorting(column) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!usesLocalData.value) {
|
if (!usesLocalData.value) {
|
||||||
if (pagination.value)
|
|
||||||
pagination.value.currentPage = 1
|
|
||||||
mapDataToRows()
|
mapDataToRows()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -328,10 +326,7 @@ async function pageChange(page) {
|
|||||||
await mapDataToRows()
|
await mapDataToRows()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refresh(isPreservePage = false) {
|
async function refresh() {
|
||||||
if (pagination.value && !isPreservePage)
|
|
||||||
pagination.value.currentPage = 1
|
|
||||||
|
|
||||||
await mapDataToRows()
|
await mapDataToRows()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,40 +0,0 @@
|
|||||||
APP_ENV=production
|
|
||||||
APP_KEY=base64:kgk/4DW1vEVy7aEvet5FPp5un6PIGe/so8H0mvoUtW0=
|
|
||||||
APP_DEBUG=true
|
|
||||||
APP_LOG_LEVEL=debug
|
|
||||||
APP_URL=http://crater.test
|
|
||||||
|
|
||||||
DB_CONNECTION=mysql
|
|
||||||
DB_HOST=127.0.0.1
|
|
||||||
DB_PORT=3306
|
|
||||||
DB_DATABASE=crater
|
|
||||||
DB_USERNAME=crater
|
|
||||||
DB_PASSWORD=crater
|
|
||||||
|
|
||||||
BROADCAST_DRIVER=log
|
|
||||||
CACHE_DRIVER=file
|
|
||||||
QUEUE_DRIVER=sync
|
|
||||||
SESSION_DRIVER=cookie
|
|
||||||
SESSION_LIFETIME=1440
|
|
||||||
|
|
||||||
REDIS_HOST=127.0.0.1
|
|
||||||
REDIS_PASSWORD=null
|
|
||||||
REDIS_PORT=6379
|
|
||||||
|
|
||||||
MAIL_DRIVER=smtp
|
|
||||||
MAIL_HOST=
|
|
||||||
MAIL_PORT=
|
|
||||||
MAIL_USERNAME=
|
|
||||||
MAIL_PASSWORD=
|
|
||||||
MAIL_ENCRYPTION=
|
|
||||||
|
|
||||||
PUSHER_APP_ID=
|
|
||||||
PUSHER_KEY=
|
|
||||||
PUSHER_SECRET=
|
|
||||||
|
|
||||||
SANCTUM_STATEFUL_DOMAINS=crater.test
|
|
||||||
SESSION_DOMAIN=crater.test
|
|
||||||
|
|
||||||
TRUSTED_PROXIES="*"
|
|
||||||
|
|
||||||
CRON_JOB_AUTH_TOKEN=""
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
FROM php:8.1-fpm
|
|
||||||
|
|
||||||
# Install system dependencies
|
|
||||||
RUN apt-get update && apt-get install -y \
|
|
||||||
git \
|
|
||||||
curl \
|
|
||||||
libpng-dev \
|
|
||||||
libonig-dev \
|
|
||||||
libxml2-dev \
|
|
||||||
zip \
|
|
||||||
unzip \
|
|
||||||
libzip-dev \
|
|
||||||
libmagickwand-dev \
|
|
||||||
mariadb-client \
|
|
||||||
npm
|
|
||||||
|
|
||||||
# Clear cache
|
|
||||||
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
RUN pecl install imagick \
|
|
||||||
&& docker-php-ext-enable imagick
|
|
||||||
|
|
||||||
# Install PHP extensions
|
|
||||||
RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl bcmath gd
|
|
||||||
|
|
||||||
# Get latest Composer
|
|
||||||
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
|
||||||
|
|
||||||
# Create system user to run Composer and Artisan Commands
|
|
||||||
RUN useradd -G www-data,root -u 1000 -d /home/crater-user crater-user
|
|
||||||
RUN mkdir -p /home/crater-user/.composer && \
|
|
||||||
chown -R crater-user:crater-user /home/crater-user
|
|
||||||
|
|
||||||
# Mounted volumes
|
|
||||||
COPY ./ /var/www
|
|
||||||
COPY ./docker-compose/php/uploads.ini /usr/local/etc/php/conf.d/uploads.ini
|
|
||||||
COPY ./uffizzi/.env.example /var/www/.env
|
|
||||||
|
|
||||||
# Set working directory
|
|
||||||
WORKDIR /var/www
|
|
||||||
|
|
||||||
RUN chown -R crater-user:crater-user ./
|
|
||||||
RUN chmod -R 775 composer.json composer.lock \
|
|
||||||
composer.lock storage/framework/ \
|
|
||||||
storage/logs/ bootstrap/cache/ /home/crater-user/.composer
|
|
||||||
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,68 +0,0 @@
|
|||||||
FROM php:8.1-fpm as build
|
|
||||||
|
|
||||||
# Install system dependencies
|
|
||||||
RUN apt-get update && apt-get install -y \
|
|
||||||
git \
|
|
||||||
curl \
|
|
||||||
libpng-dev \
|
|
||||||
libonig-dev \
|
|
||||||
libxml2-dev \
|
|
||||||
zip \
|
|
||||||
unzip \
|
|
||||||
libzip-dev \
|
|
||||||
libmagickwand-dev \
|
|
||||||
mariadb-client
|
|
||||||
|
|
||||||
# Clear cache
|
|
||||||
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
RUN pecl install imagick \
|
|
||||||
&& docker-php-ext-enable imagick
|
|
||||||
|
|
||||||
# Install PHP extensions
|
|
||||||
RUN docker-php-ext-install pdo_mysql mbstring zip exif pcntl bcmath gd
|
|
||||||
|
|
||||||
# Get latest Composer
|
|
||||||
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
|
||||||
|
|
||||||
# Create system user to run Composer and Artisan Commands
|
|
||||||
RUN useradd -G www-data,root -u 1000 -d /home/crater-user crater-user
|
|
||||||
RUN mkdir -p /home/crater-user/.composer && \
|
|
||||||
chown -R crater-user:crater-user /home/crater-user
|
|
||||||
|
|
||||||
# Mounted volumes
|
|
||||||
COPY ./ /var/www
|
|
||||||
COPY ./docker-compose/php/uploads.ini /usr/local/etc/php/conf.d/uploads.ini
|
|
||||||
COPY ./uffizzi/.env.example /var/www/.env
|
|
||||||
|
|
||||||
# Set working directory
|
|
||||||
WORKDIR /var/www
|
|
||||||
|
|
||||||
RUN chown -R crater-user:crater-user ./
|
|
||||||
RUN chmod -R 775 composer.json composer.lock \
|
|
||||||
composer.lock storage/framework/ \
|
|
||||||
storage/logs/ bootstrap/cache/ /home/crater-user/.composer
|
|
||||||
|
|
||||||
RUN composer config --no-plugins allow-plugins.pestphp/pest-plugin true && \
|
|
||||||
composer install --no-interaction --prefer-dist --optimize-autoloader && \
|
|
||||||
php artisan storage:link || true && \
|
|
||||||
php artisan key:generate
|
|
||||||
|
|
||||||
FROM php:8.0-fpm-alpine
|
|
||||||
|
|
||||||
RUN apk add --no-cache \
|
|
||||||
php8-bcmath
|
|
||||||
|
|
||||||
RUN docker-php-ext-install pdo pdo_mysql bcmath
|
|
||||||
|
|
||||||
COPY docker-compose/crontab /etc/crontabs/root
|
|
||||||
|
|
||||||
# Mounted volumes
|
|
||||||
COPY --from=build /var/www /var/www
|
|
||||||
|
|
||||||
RUN chown -R $(whoami):$(whoami) /var/www/
|
|
||||||
RUN chmod -R 775 /var/www/
|
|
||||||
RUN chown -R $(whoami):$(whoami) /var/log/
|
|
||||||
RUN chmod -R 775 /var/log/
|
|
||||||
|
|
||||||
CMD ["crond", "-f"]
|
|
||||||
@ -1,58 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
x-uffizzi:
|
|
||||||
ingress:
|
|
||||||
service: nginx
|
|
||||||
port: 80
|
|
||||||
|
|
||||||
services:
|
|
||||||
app:
|
|
||||||
image: "${APP_IMAGE}"
|
|
||||||
restart: unless-stopped
|
|
||||||
working_dir: /var/www/
|
|
||||||
command: ["-c","
|
|
||||||
composer config --no-plugins allow-plugins.pestphp/pest-plugin true &&
|
|
||||||
composer install --no-interaction --prefer-dist --optimize-autoloader &&
|
|
||||||
php artisan storage:link || true &&
|
|
||||||
php artisan key:generate --force &&
|
|
||||||
php-fpm",
|
|
||||||
]
|
|
||||||
entrypoint: /bin/sh
|
|
||||||
depends_on:
|
|
||||||
- db
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
memory: 1000m
|
|
||||||
|
|
||||||
db:
|
|
||||||
image: mariadb
|
|
||||||
restart: always
|
|
||||||
environment:
|
|
||||||
MYSQL_USER: crater
|
|
||||||
MYSQL_PASSWORD: crater
|
|
||||||
MYSQL_DATABASE: crater
|
|
||||||
MYSQL_ROOT_PASSWORD: crater
|
|
||||||
ports:
|
|
||||||
- '33006:3306'
|
|
||||||
deploy:
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
memory: 500m
|
|
||||||
|
|
||||||
nginx:
|
|
||||||
image: "${NGINX_IMAGE}"
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- 80:80
|
|
||||||
depends_on:
|
|
||||||
- app
|
|
||||||
resources:
|
|
||||||
limits:
|
|
||||||
memory: 500m
|
|
||||||
|
|
||||||
cron:
|
|
||||||
image: "${CROND_IMAGE}"
|
|
||||||
restart: always
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
ARG BASE_IMAGE
|
|
||||||
|
|
||||||
FROM $BASE_IMAGE as build
|
|
||||||
FROM nginx:1.17-alpine
|
|
||||||
|
|
||||||
RUN rm /etc/nginx/conf.d/default.conf
|
|
||||||
|
|
||||||
COPY --from=build /var/www /var/www
|
|
||||||
COPY ./uffizzi/nginx/nginx /etc/nginx/conf.d/
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
server {
|
|
||||||
client_max_body_size 64M;
|
|
||||||
listen 80;
|
|
||||||
index index.php index.html;
|
|
||||||
error_log /var/log/nginx/error.log;
|
|
||||||
access_log /var/log/nginx/access.log;
|
|
||||||
root /var/www/public;
|
|
||||||
location ~ \.php$ {
|
|
||||||
try_files $uri =404;
|
|
||||||
fastcgi_split_path_info ^(.+\.php)(/.+)$;
|
|
||||||
fastcgi_pass localhost:9000;
|
|
||||||
fastcgi_index index.php;
|
|
||||||
include fastcgi_params;
|
|
||||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
|
||||||
fastcgi_param PATH_INFO $fastcgi_path_info;
|
|
||||||
fastcgi_read_timeout 300;
|
|
||||||
}
|
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ /index.php?$query_string;
|
|
||||||
gzip_static on;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user