mirror of
https://github.com/crater-invoice/crater.git
synced 2025-10-28 12:11:08 -04:00
Compare commits
141 Commits
mail-sende
...
e5afc93efa
| Author | SHA1 | Date | |
|---|---|---|---|
| e5afc93efa | |||
| 9f9e45a76a | |||
| 0e7c218819 | |||
| b18ccc1b34 | |||
| 80a209284d | |||
| 7152a7b74a | |||
| 77bb074f84 | |||
| 3e145211a5 | |||
| 7c556b8427 | |||
| ff9356eb49 | |||
| 11caa7bf51 | |||
| 85a87e5f2f | |||
| 4a4353d779 | |||
| 07bf7d3d5c | |||
| ef11dc4440 | |||
| fcd992179e | |||
| d057b27560 | |||
| 6c66f0634b | |||
| b6eb1eef78 | |||
| da184cb13e | |||
| ff8519764e | |||
| 55163888b3 | |||
| 0ea6bce713 | |||
| c8ea2a7f3d | |||
| 078d928206 | |||
| 01f01b8607 | |||
| 1314821bb1 | |||
| 40d2d7435b | |||
| 36f334dc87 | |||
| dfd13bf9de | |||
| afb8cab71a | |||
| 6f0c9c6923 | |||
| a7b1fe87ea | |||
| a2970402b9 | |||
| 2934b9fc3f | |||
| ebf19636e1 | |||
| cbc13d4884 | |||
| b02803e7c5 | |||
| 194a8cb235 | |||
| 8fd045e378 | |||
| 7a11fbd02a | |||
| 5bc213299f | |||
| 2b5b4ab603 | |||
| 8a47cab96c | |||
| 606fed3e68 | |||
| d227ad4e6b | |||
| 835d61d73c | |||
| 6bb89db25e | |||
| 3f0fd04ad3 | |||
| 6c3e1b1135 | |||
| e331bc18ab | |||
| 67549fd299 | |||
| e6cea15a5b | |||
| 9471cfbca3 | |||
| e0da5e8eb8 | |||
| 1328e36306 | |||
| 5a5ed65202 | |||
| 598e5801bc | |||
| 427fb7675f | |||
| 4ce313b10a | |||
| 12d4107c13 | |||
| 41ba8fc849 | |||
| 2ba2d83478 | |||
| f421cd1677 | |||
| 16b4533892 | |||
| 3afdfe3fda | |||
| ebdfd83b04 | |||
| 26ba2d8d28 | |||
| 22377bc407 | |||
| 65b7a2c78e | |||
| d8ca49572b | |||
| f28d67629f | |||
| fa9c0824d2 | |||
| d8ad7d1c09 | |||
| f3eba0ca98 | |||
| 0046ce1675 | |||
| 1ea4048e98 | |||
| 3922da67db | |||
| d939c7340f | |||
| a1540dcbc1 | |||
| df89e1d248 | |||
| c4d79a8b63 | |||
| 5d875af7be | |||
| 9734ccf9fa | |||
| 8080fe86ca | |||
| 43b3f00dff | |||
| 9f3b39a685 | |||
| 309b2e0888 | |||
| ca60a4830d | |||
| ee96724b81 | |||
| c3250d6141 | |||
| d2a8477087 | |||
| 718657b0a4 | |||
| 1acac8f129 | |||
| 176b207335 | |||
| 77d7ae0fe4 | |||
| fc222fc6de | |||
| fa9d28e66b | |||
| a87ef11edc | |||
| 221f272da9 | |||
| b022cbad59 | |||
| d342bc9771 | |||
| c236012f4e | |||
| c50a2a5988 | |||
| f07f488148 | |||
| 46ba42478f | |||
| ddfea9d827 | |||
| 25aa4ac198 | |||
| 2081580640 | |||
| b20277d1b7 | |||
| 9fded639b9 | |||
| a19266a29c | |||
| d07d2243a9 | |||
| 19a23983ab | |||
| 3545ebb7a4 | |||
| e0ab4136a9 | |||
| 7ee0ceba0c | |||
| 2ca332f33c | |||
| fd3a1524ad | |||
| 889df7b9a6 | |||
| 73f36981e4 | |||
| e3460384d0 | |||
| a793d53b31 | |||
| 59854f016b | |||
| 411e2bf7b7 | |||
| fc46391dec | |||
| 15bfbfdaf3 | |||
| 9d7cc7548a | |||
| 7e460249bc | |||
| fd68cea4de | |||
| 0268b808d5 | |||
| 532422feca | |||
| f3cb1f6af6 | |||
| 5b0e1fc5a8 | |||
| 6f68f57791 | |||
| 6db63b3aae | |||
| c8e7378725 | |||
| cf1e90aa40 | |||
| 3d476f66f9 | |||
| 5f0925416e | |||
| a3a1f51d34 |
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
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -16,5 +16,3 @@ Homestead.yaml
|
|||||||
.gitkeep
|
.gitkeep
|
||||||
/public/docs
|
/public/docs
|
||||||
/.scribe
|
/.scribe
|
||||||
!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);
|
||||||
|
|
||||||
|
|||||||
@ -1,24 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Crater\Http\Controllers\V1\Admin\MailSender;
|
|
||||||
|
|
||||||
use Crater\Http\Controllers\Controller;
|
|
||||||
use Crater\Http\Resources\MailSenderResource;
|
|
||||||
use Crater\Models\MailSender;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
|
|
||||||
class GetAllMailSendersController extends Controller
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Handle the incoming request.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function __invoke(Request $request)
|
|
||||||
{
|
|
||||||
$mailSenders = MailSender::whereCompany()->get();
|
|
||||||
|
|
||||||
return MailSenderResource::collection($mailSenders);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,98 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Crater\Http\Controllers\V1\Admin\MailSender;
|
|
||||||
|
|
||||||
use Crater\Http\Controllers\Controller;
|
|
||||||
use Crater\Http\Requests\MailSenderRequest;
|
|
||||||
use Crater\Http\Resources\MailSenderResource;
|
|
||||||
use Crater\Models\MailSender;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
|
|
||||||
class MailSenderController extends Controller
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Display a listing of the resource.
|
|
||||||
*
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function index(Request $request)
|
|
||||||
{
|
|
||||||
$this->authorize('viewAny', MailSender::class);
|
|
||||||
|
|
||||||
$limit = $request->has('limit') ? $request->limit : 10;
|
|
||||||
|
|
||||||
$mailSenders = MailSender::whereCompany()
|
|
||||||
->applyFilters($request->all())
|
|
||||||
->paginateData($limit);
|
|
||||||
|
|
||||||
return (MailSenderResource::collection($mailSenders))
|
|
||||||
->additional(['meta' => [
|
|
||||||
'mail_sender_total_count' => MailSender::whereCompany()->count(),
|
|
||||||
]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a newly created resource in storage.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function store(MailSenderRequest $request)
|
|
||||||
{
|
|
||||||
$this->authorize('create', MailSender::class);
|
|
||||||
|
|
||||||
$mailSender = MailSender::createFromRequest($request);
|
|
||||||
|
|
||||||
return new MailSenderResource($mailSender);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the specified resource.
|
|
||||||
*
|
|
||||||
* @param \Crater\Models\SenderMail $senderMail
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function show(MailSender $mailSender)
|
|
||||||
{
|
|
||||||
$this->authorize('view', $mailSender);
|
|
||||||
|
|
||||||
return new MailSenderResource($mailSender);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the specified resource in storage.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @param \Crater\Models\SenderMail $senderMail
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function update(MailSenderRequest $request, MailSender $mailSender)
|
|
||||||
{
|
|
||||||
$this->authorize('update', $mailSender);
|
|
||||||
|
|
||||||
$mailSender->updateFromRequest($request);
|
|
||||||
|
|
||||||
return new MailSenderResource($mailSender);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the specified resource from storage.
|
|
||||||
*
|
|
||||||
* @param \Crater\Models\SenderMail $senderMail
|
|
||||||
* @return \Illuminate\Http\Response
|
|
||||||
*/
|
|
||||||
public function destroy(MailSender $mailSender)
|
|
||||||
{
|
|
||||||
$this->authorize('delete', $mailSender);
|
|
||||||
|
|
||||||
if ($mailSender->is_default) {
|
|
||||||
return respondJson('You can\'t remove default mail sender.', 'You can\'t remove default mail sender.');
|
|
||||||
}
|
|
||||||
|
|
||||||
$mailSender->delete();
|
|
||||||
|
|
||||||
return response()->json([
|
|
||||||
'success' => true,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -2,25 +2,24 @@
|
|||||||
|
|
||||||
namespace Crater\Http\Controllers\V1\Admin\Report;
|
namespace Crater\Http\Controllers\V1\Admin\Report;
|
||||||
|
|
||||||
use PDF;
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use Crater\Http\Controllers\Controller;
|
||||||
use Crater\Models\Company;
|
use Crater\Models\Company;
|
||||||
use Crater\Models\Currency;
|
use Crater\Models\CompanySetting;
|
||||||
use Crater\Models\Customer;
|
use Crater\Models\Customer;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Crater\Models\CompanySetting;
|
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
use Crater\Http\Controllers\Controller;
|
use PDF;
|
||||||
|
|
||||||
class CustomerSalesReportController extends Controller
|
class CustomerSalesReportController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Handle the incoming request.
|
* Handle the incoming request.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Illuminate\Http\Request $request
|
||||||
* @param string $hash
|
* @param string $hash
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*/
|
*/
|
||||||
public function __invoke(Request $request, $hash)
|
public function __invoke(Request $request, $hash)
|
||||||
{
|
{
|
||||||
$company = Company::where('unique_hash', $hash)->first();
|
$company = Company::where('unique_hash', $hash)->first();
|
||||||
@ -57,7 +56,6 @@ class CustomerSalesReportController extends Controller
|
|||||||
$dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id);
|
$dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id);
|
||||||
$from_date = Carbon::createFromFormat('Y-m-d', $request->from_date)->format($dateFormat);
|
$from_date = Carbon::createFromFormat('Y-m-d', $request->from_date)->format($dateFormat);
|
||||||
$to_date = Carbon::createFromFormat('Y-m-d', $request->to_date)->format($dateFormat);
|
$to_date = Carbon::createFromFormat('Y-m-d', $request->to_date)->format($dateFormat);
|
||||||
$currency = Currency::findOrFail(CompanySetting::getSetting('currency', $company->id));
|
|
||||||
|
|
||||||
$colors = [
|
$colors = [
|
||||||
'primary_text_color',
|
'primary_text_color',
|
||||||
@ -82,7 +80,6 @@ class CustomerSalesReportController extends Controller
|
|||||||
'company' => $company,
|
'company' => $company,
|
||||||
'from_date' => $from_date,
|
'from_date' => $from_date,
|
||||||
'to_date' => $to_date,
|
'to_date' => $to_date,
|
||||||
'currency' => $currency,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$pdf = PDF::loadView('app.pdf.reports.sales-customers');
|
$pdf = PDF::loadView('app.pdf.reports.sales-customers');
|
||||||
|
|||||||
@ -2,25 +2,24 @@
|
|||||||
|
|
||||||
namespace Crater\Http\Controllers\V1\Admin\Report;
|
namespace Crater\Http\Controllers\V1\Admin\Report;
|
||||||
|
|
||||||
use PDF;
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Crater\Models\Company;
|
|
||||||
use Crater\Models\Expense;
|
|
||||||
use Crater\Models\Currency;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Crater\Models\CompanySetting;
|
|
||||||
use Illuminate\Support\Facades\App;
|
|
||||||
use Crater\Http\Controllers\Controller;
|
use Crater\Http\Controllers\Controller;
|
||||||
|
use Crater\Models\Company;
|
||||||
|
use Crater\Models\CompanySetting;
|
||||||
|
use Crater\Models\Expense;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\App;
|
||||||
|
use PDF;
|
||||||
|
|
||||||
class ExpensesReportController extends Controller
|
class ExpensesReportController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Handle the incoming request.
|
* Handle the incoming request.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Illuminate\Http\Request $request
|
||||||
* @param string $hash
|
* @param string $hash
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*/
|
*/
|
||||||
public function __invoke(Request $request, $hash)
|
public function __invoke(Request $request, $hash)
|
||||||
{
|
{
|
||||||
$company = Company::where('unique_hash', $hash)->first();
|
$company = Company::where('unique_hash', $hash)->first();
|
||||||
@ -44,7 +43,6 @@ class ExpensesReportController extends Controller
|
|||||||
$dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id);
|
$dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id);
|
||||||
$from_date = Carbon::createFromFormat('Y-m-d', $request->from_date)->format($dateFormat);
|
$from_date = Carbon::createFromFormat('Y-m-d', $request->from_date)->format($dateFormat);
|
||||||
$to_date = Carbon::createFromFormat('Y-m-d', $request->to_date)->format($dateFormat);
|
$to_date = Carbon::createFromFormat('Y-m-d', $request->to_date)->format($dateFormat);
|
||||||
$currency = Currency::findOrFail(CompanySetting::getSetting('currency', $company->id));
|
|
||||||
|
|
||||||
$colors = [
|
$colors = [
|
||||||
'primary_text_color',
|
'primary_text_color',
|
||||||
@ -68,7 +66,6 @@ class ExpensesReportController extends Controller
|
|||||||
'company' => $company,
|
'company' => $company,
|
||||||
'from_date' => $from_date,
|
'from_date' => $from_date,
|
||||||
'to_date' => $to_date,
|
'to_date' => $to_date,
|
||||||
'currency' => $currency,
|
|
||||||
]);
|
]);
|
||||||
$pdf = PDF::loadView('app.pdf.reports.expenses');
|
$pdf = PDF::loadView('app.pdf.reports.expenses');
|
||||||
|
|
||||||
|
|||||||
@ -2,25 +2,24 @@
|
|||||||
|
|
||||||
namespace Crater\Http\Controllers\V1\Admin\Report;
|
namespace Crater\Http\Controllers\V1\Admin\Report;
|
||||||
|
|
||||||
use PDF;
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Crater\Models\Company;
|
|
||||||
use Crater\Models\Currency;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Crater\Models\InvoiceItem;
|
|
||||||
use Crater\Models\CompanySetting;
|
|
||||||
use Illuminate\Support\Facades\App;
|
|
||||||
use Crater\Http\Controllers\Controller;
|
use Crater\Http\Controllers\Controller;
|
||||||
|
use Crater\Models\Company;
|
||||||
|
use Crater\Models\CompanySetting;
|
||||||
|
use Crater\Models\InvoiceItem;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\App;
|
||||||
|
use PDF;
|
||||||
|
|
||||||
class ItemSalesReportController extends Controller
|
class ItemSalesReportController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Handle the incoming request.
|
* Handle the incoming request.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Illuminate\Http\Request $request
|
||||||
* @param string $hash
|
* @param string $hash
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*/
|
*/
|
||||||
public function __invoke(Request $request, $hash)
|
public function __invoke(Request $request, $hash)
|
||||||
{
|
{
|
||||||
$company = Company::where('unique_hash', $hash)->first();
|
$company = Company::where('unique_hash', $hash)->first();
|
||||||
@ -44,7 +43,6 @@ class ItemSalesReportController extends Controller
|
|||||||
$dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id);
|
$dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id);
|
||||||
$from_date = Carbon::createFromFormat('Y-m-d', $request->from_date)->format($dateFormat);
|
$from_date = Carbon::createFromFormat('Y-m-d', $request->from_date)->format($dateFormat);
|
||||||
$to_date = Carbon::createFromFormat('Y-m-d', $request->to_date)->format($dateFormat);
|
$to_date = Carbon::createFromFormat('Y-m-d', $request->to_date)->format($dateFormat);
|
||||||
$currency = Currency::findOrFail(CompanySetting::getSetting('currency', $company->id));
|
|
||||||
|
|
||||||
$colors = [
|
$colors = [
|
||||||
'primary_text_color',
|
'primary_text_color',
|
||||||
@ -68,7 +66,6 @@ class ItemSalesReportController extends Controller
|
|||||||
'company' => $company,
|
'company' => $company,
|
||||||
'from_date' => $from_date,
|
'from_date' => $from_date,
|
||||||
'to_date' => $to_date,
|
'to_date' => $to_date,
|
||||||
'currency' => $currency,
|
|
||||||
]);
|
]);
|
||||||
$pdf = PDF::loadView('app.pdf.reports.sales-items');
|
$pdf = PDF::loadView('app.pdf.reports.sales-items');
|
||||||
|
|
||||||
|
|||||||
@ -2,26 +2,25 @@
|
|||||||
|
|
||||||
namespace Crater\Http\Controllers\V1\Admin\Report;
|
namespace Crater\Http\Controllers\V1\Admin\Report;
|
||||||
|
|
||||||
use PDF;
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use Crater\Http\Controllers\Controller;
|
||||||
use Crater\Models\Company;
|
use Crater\Models\Company;
|
||||||
|
use Crater\Models\CompanySetting;
|
||||||
use Crater\Models\Expense;
|
use Crater\Models\Expense;
|
||||||
use Crater\Models\Payment;
|
use Crater\Models\Payment;
|
||||||
use Crater\Models\Currency;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Crater\Models\CompanySetting;
|
|
||||||
use Illuminate\Support\Facades\App;
|
use Illuminate\Support\Facades\App;
|
||||||
use Crater\Http\Controllers\Controller;
|
use PDF;
|
||||||
|
|
||||||
class ProfitLossReportController extends Controller
|
class ProfitLossReportController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Handle the incoming request.
|
* Handle the incoming request.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Illuminate\Http\Request $request
|
||||||
* @param string $hash
|
* @param string $hash
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*/
|
*/
|
||||||
public function __invoke(Request $request, $hash)
|
public function __invoke(Request $request, $hash)
|
||||||
{
|
{
|
||||||
$company = Company::where('unique_hash', $hash)->first();
|
$company = Company::where('unique_hash', $hash)->first();
|
||||||
@ -50,8 +49,6 @@ class ProfitLossReportController extends Controller
|
|||||||
$dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id);
|
$dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id);
|
||||||
$from_date = Carbon::createFromFormat('Y-m-d', $request->from_date)->format($dateFormat);
|
$from_date = Carbon::createFromFormat('Y-m-d', $request->from_date)->format($dateFormat);
|
||||||
$to_date = Carbon::createFromFormat('Y-m-d', $request->to_date)->format($dateFormat);
|
$to_date = Carbon::createFromFormat('Y-m-d', $request->to_date)->format($dateFormat);
|
||||||
$currency = Currency::findOrFail(CompanySetting::getSetting('currency', $company->id));
|
|
||||||
|
|
||||||
|
|
||||||
$colors = [
|
$colors = [
|
||||||
'primary_text_color',
|
'primary_text_color',
|
||||||
@ -77,7 +74,6 @@ class ProfitLossReportController extends Controller
|
|||||||
'company' => $company,
|
'company' => $company,
|
||||||
'from_date' => $from_date,
|
'from_date' => $from_date,
|
||||||
'to_date' => $to_date,
|
'to_date' => $to_date,
|
||||||
'currency' => $currency,
|
|
||||||
]);
|
]);
|
||||||
$pdf = PDF::loadView('app.pdf.reports.profit-loss');
|
$pdf = PDF::loadView('app.pdf.reports.profit-loss');
|
||||||
|
|
||||||
|
|||||||
@ -2,25 +2,24 @@
|
|||||||
|
|
||||||
namespace Crater\Http\Controllers\V1\Admin\Report;
|
namespace Crater\Http\Controllers\V1\Admin\Report;
|
||||||
|
|
||||||
use PDF;
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Crater\Models\Tax;
|
|
||||||
use Crater\Models\Company;
|
|
||||||
use Crater\Models\Currency;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Crater\Models\CompanySetting;
|
|
||||||
use Illuminate\Support\Facades\App;
|
|
||||||
use Crater\Http\Controllers\Controller;
|
use Crater\Http\Controllers\Controller;
|
||||||
|
use Crater\Models\Company;
|
||||||
|
use Crater\Models\CompanySetting;
|
||||||
|
use Crater\Models\Tax;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\App;
|
||||||
|
use PDF;
|
||||||
|
|
||||||
class TaxSummaryReportController extends Controller
|
class TaxSummaryReportController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Handle the incoming request.
|
* Handle the incoming request.
|
||||||
*
|
*
|
||||||
* @param \Illuminate\Http\Request $request
|
* @param \Illuminate\Http\Request $request
|
||||||
* @param string $hash
|
* @param string $hash
|
||||||
* @return \Illuminate\Http\JsonResponse
|
* @return \Illuminate\Http\JsonResponse
|
||||||
*/
|
*/
|
||||||
public function __invoke(Request $request, $hash)
|
public function __invoke(Request $request, $hash)
|
||||||
{
|
{
|
||||||
$company = Company::where('unique_hash', $hash)->first();
|
$company = Company::where('unique_hash', $hash)->first();
|
||||||
@ -45,8 +44,6 @@ class TaxSummaryReportController extends Controller
|
|||||||
$dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id);
|
$dateFormat = CompanySetting::getSetting('carbon_date_format', $company->id);
|
||||||
$from_date = Carbon::createFromFormat('Y-m-d', $request->from_date)->format($dateFormat);
|
$from_date = Carbon::createFromFormat('Y-m-d', $request->from_date)->format($dateFormat);
|
||||||
$to_date = Carbon::createFromFormat('Y-m-d', $request->to_date)->format($dateFormat);
|
$to_date = Carbon::createFromFormat('Y-m-d', $request->to_date)->format($dateFormat);
|
||||||
$currency = Currency::findOrFail(CompanySetting::getSetting('currency', $company->id));
|
|
||||||
|
|
||||||
|
|
||||||
$colors = [
|
$colors = [
|
||||||
'primary_text_color',
|
'primary_text_color',
|
||||||
@ -71,7 +68,6 @@ class TaxSummaryReportController extends Controller
|
|||||||
'company' => $company,
|
'company' => $company,
|
||||||
'from_date' => $from_date,
|
'from_date' => $from_date,
|
||||||
'to_date' => $to_date,
|
'to_date' => $to_date,
|
||||||
'currency' => $currency,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$pdf = PDF::loadView('app.pdf.reports.tax-summary');
|
$pdf = PDF::loadView('app.pdf.reports.tax-summary');
|
||||||
|
|||||||
@ -3,29 +3,80 @@
|
|||||||
namespace Crater\Http\Controllers\V1\Admin\Settings;
|
namespace Crater\Http\Controllers\V1\Admin\Settings;
|
||||||
|
|
||||||
use Crater\Http\Controllers\Controller;
|
use Crater\Http\Controllers\Controller;
|
||||||
use Crater\Http\Requests\TestMailDriverRequest;
|
use Crater\Http\Requests\MailEnvironmentRequest;
|
||||||
use Crater\Mail\TestMail;
|
use Crater\Mail\TestMail;
|
||||||
use Crater\Models\MailSender;
|
use Crater\Models\Setting;
|
||||||
|
use Crater\Space\EnvironmentManager;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Mail;
|
use Mail;
|
||||||
|
|
||||||
class MailConfigurationController extends Controller
|
class MailConfigurationController extends Controller
|
||||||
{
|
{
|
||||||
public function TestMailDriver(TestMailDriverRequest $request)
|
/**
|
||||||
|
* @var EnvironmentManager
|
||||||
|
*/
|
||||||
|
protected $environmentManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param EnvironmentManager $environmentManager
|
||||||
|
*/
|
||||||
|
public function __construct(EnvironmentManager $environmentManager)
|
||||||
|
{
|
||||||
|
$this->environmentManager = $environmentManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param MailEnvironmentRequest $request
|
||||||
|
* @return JsonResponse
|
||||||
|
*/
|
||||||
|
public function saveMailEnvironment(MailEnvironmentRequest $request)
|
||||||
{
|
{
|
||||||
$this->authorize('manage email config');
|
$this->authorize('manage email config');
|
||||||
|
|
||||||
MailSender::setMailConfiguration($request->mail_sender_id);
|
$setting = Setting::getSetting('profile_complete');
|
||||||
|
$results = $this->environmentManager->saveMailVariables($request);
|
||||||
|
|
||||||
Mail::to($request->to)->send(new TestMail($request->subject, $request->message));
|
if ($setting !== 'COMPLETED') {
|
||||||
|
Setting::setSetting('profile_complete', 4);
|
||||||
|
}
|
||||||
|
|
||||||
return response()->json([
|
return response()->json($results);
|
||||||
'success' => true,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMailDrivers(Request $request)
|
public function getMailEnvironment()
|
||||||
{
|
{
|
||||||
|
$this->authorize('manage email config');
|
||||||
|
|
||||||
|
$MailData = [
|
||||||
|
'mail_driver' => config('mail.driver'),
|
||||||
|
'mail_host' => config('mail.host'),
|
||||||
|
'mail_port' => config('mail.port'),
|
||||||
|
'mail_username' => config('mail.username'),
|
||||||
|
'mail_password' => config('mail.password'),
|
||||||
|
'mail_encryption' => config('mail.encryption'),
|
||||||
|
'from_name' => config('mail.from.name'),
|
||||||
|
'from_mail' => config('mail.from.address'),
|
||||||
|
'mail_mailgun_endpoint' => config('services.mailgun.endpoint'),
|
||||||
|
'mail_mailgun_domain' => config('services.mailgun.domain'),
|
||||||
|
'mail_mailgun_secret' => config('services.mailgun.secret'),
|
||||||
|
'mail_ses_key' => config('services.ses.key'),
|
||||||
|
'mail_ses_secret' => config('services.ses.secret'),
|
||||||
|
];
|
||||||
|
|
||||||
|
|
||||||
|
return response()->json($MailData);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return JsonResponse
|
||||||
|
*/
|
||||||
|
public function getMailDrivers()
|
||||||
|
{
|
||||||
|
$this->authorize('manage email config');
|
||||||
|
|
||||||
$drivers = [
|
$drivers = [
|
||||||
'smtp',
|
'smtp',
|
||||||
'mail',
|
'mail',
|
||||||
@ -36,4 +87,21 @@ class MailConfigurationController extends Controller
|
|||||||
|
|
||||||
return response()->json($drivers);
|
return response()->json($drivers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testEmailConfig(Request $request)
|
||||||
|
{
|
||||||
|
$this->authorize('manage email config');
|
||||||
|
|
||||||
|
$this->validate($request, [
|
||||||
|
'to' => 'required|email',
|
||||||
|
'subject' => 'required',
|
||||||
|
'message' => 'required',
|
||||||
|
]);
|
||||||
|
|
||||||
|
Mail::to($request->to)->send(new TestMail($request->subject, $request->message));
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,6 @@ use Crater\Models\CompanySetting;
|
|||||||
use Crater\Models\Customer;
|
use Crater\Models\Customer;
|
||||||
use Crater\Models\EmailLog;
|
use Crater\Models\EmailLog;
|
||||||
use Crater\Models\Estimate;
|
use Crater\Models\Estimate;
|
||||||
use Crater\Models\MailSender;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class EstimatePdfController extends Controller
|
class EstimatePdfController extends Controller
|
||||||
@ -28,16 +27,14 @@ class EstimatePdfController extends Controller
|
|||||||
);
|
);
|
||||||
|
|
||||||
if ($notifyEstimateViewed == 'YES') {
|
if ($notifyEstimateViewed == 'YES') {
|
||||||
$notificationEmail = CompanySetting::getSetting('notification_email', $estimate->company_id);
|
|
||||||
$mailSender = MailSender::where('company_id', $estimate->company_id)->where('is_default', true)->first();
|
|
||||||
MailSender::setMailConfiguration($mailSender->id);
|
|
||||||
|
|
||||||
$data['from_address'] = $mailSender->from_address;
|
|
||||||
$data['from_name'] = $mailSender->from_name;
|
|
||||||
$data['user'] = Customer::find($estimate->customer_id)->toArray();
|
|
||||||
$data['estimate'] = Estimate::findOrFail($estimate->id)->toArray();
|
$data['estimate'] = Estimate::findOrFail($estimate->id)->toArray();
|
||||||
|
$data['user'] = Customer::find($estimate->customer_id)->toArray();
|
||||||
|
$notificationEmail = CompanySetting::getSetting(
|
||||||
|
'notification_email',
|
||||||
|
$estimate->company_id
|
||||||
|
);
|
||||||
|
|
||||||
send_mail(new EstimateViewedMail($data), $mailSender, $notificationEmail);
|
\Mail::to($notificationEmail)->send(new EstimateViewedMail($data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,6 @@ use Crater\Models\CompanySetting;
|
|||||||
use Crater\Models\Customer;
|
use Crater\Models\Customer;
|
||||||
use Crater\Models\EmailLog;
|
use Crater\Models\EmailLog;
|
||||||
use Crater\Models\Invoice;
|
use Crater\Models\Invoice;
|
||||||
use Crater\Models\MailSender;
|
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
class InvoicePdfController extends Controller
|
class InvoicePdfController extends Controller
|
||||||
@ -29,16 +28,14 @@ class InvoicePdfController extends Controller
|
|||||||
);
|
);
|
||||||
|
|
||||||
if ($notifyInvoiceViewed == 'YES') {
|
if ($notifyInvoiceViewed == 'YES') {
|
||||||
$notificationEmail = CompanySetting::getSetting('notification_email', $invoice->company_id);
|
|
||||||
$mailSender = MailSender::where('company_id', $invoice->company_id)->where('is_default', true)->first();
|
|
||||||
MailSender::setMailConfiguration($mailSender->id);
|
|
||||||
|
|
||||||
$data['from_address'] = $mailSender->from_address;
|
|
||||||
$data['from_name'] = $mailSender->from_name;
|
|
||||||
$data['invoice'] = Invoice::findOrFail($invoice->id)->toArray();
|
$data['invoice'] = Invoice::findOrFail($invoice->id)->toArray();
|
||||||
$data['user'] = Customer::find($invoice->customer_id)->toArray();
|
$data['user'] = Customer::find($invoice->customer_id)->toArray();
|
||||||
|
$notificationEmail = CompanySetting::getSetting(
|
||||||
|
'notification_email',
|
||||||
|
$invoice->company_id
|
||||||
|
);
|
||||||
|
|
||||||
send_mail(new InvoiceViewedMail($data), $mailSender, $notificationEmail);
|
\Mail::to($notificationEmail)->send(new InvoiceViewedMail($data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,6 @@ namespace Crater\Http\Middleware;
|
|||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
use Crater\Models\FileDisk;
|
use Crater\Models\FileDisk;
|
||||||
use Crater\Models\MailSender;
|
|
||||||
|
|
||||||
class ConfigMiddleware
|
class ConfigMiddleware
|
||||||
{
|
{
|
||||||
@ -29,12 +28,6 @@ class ConfigMiddleware
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$default_mail_sender = MailSender::where('company_id', $request->header('company'))->where('is_default', true)->first();
|
|
||||||
|
|
||||||
if ($default_mail_sender) {
|
|
||||||
$default_mail_sender->setMailConfiguration($default_mail_sender->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,85 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Crater\Http\Requests;
|
|
||||||
|
|
||||||
use Illuminate\Validation\Rule;
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
|
|
||||||
class MailSenderRequest extends FormRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Determine if the user is authorized to make this request.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function authorize()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function rules()
|
|
||||||
{
|
|
||||||
$rules = [
|
|
||||||
'name' => [
|
|
||||||
'required',
|
|
||||||
Rule::unique('mail_senders')
|
|
||||||
->where('company_id', $this->header('company'))
|
|
||||||
],
|
|
||||||
'driver' => [
|
|
||||||
'required',
|
|
||||||
],
|
|
||||||
'is_default' => [
|
|
||||||
'nullable'
|
|
||||||
],
|
|
||||||
'bcc' => [
|
|
||||||
'nullable'
|
|
||||||
],
|
|
||||||
'cc' => [
|
|
||||||
'nullable'
|
|
||||||
],
|
|
||||||
'from_address' => [
|
|
||||||
'nullable'
|
|
||||||
],
|
|
||||||
'from_name' => [
|
|
||||||
'nullable'
|
|
||||||
],
|
|
||||||
'settings' => [
|
|
||||||
'nullable'
|
|
||||||
],
|
|
||||||
'settings.*' => [
|
|
||||||
'nullable'
|
|
||||||
]
|
|
||||||
];
|
|
||||||
|
|
||||||
if ($this->isMethod('PUT')) {
|
|
||||||
$rules['name'] = [
|
|
||||||
'nullable',
|
|
||||||
Rule::unique('mail_senders')
|
|
||||||
->ignore($this->route('mail_sender')->id)
|
|
||||||
->where('company_id', $this->header('company'))
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $rules;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMailSenderPayload()
|
|
||||||
{
|
|
||||||
$data = $this->validated();
|
|
||||||
|
|
||||||
if ($data['settings'] && $data['settings']['encryption'] == 'none') {
|
|
||||||
$data['settings']['encryption'] = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
return collect($data)
|
|
||||||
->merge([
|
|
||||||
'company_id' => $this->header('company'),
|
|
||||||
])
|
|
||||||
->toArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -30,7 +30,7 @@ class SendEstimatesRequest extends FormRequest
|
|||||||
'body' => [
|
'body' => [
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'mail_sender_id' => [
|
'from' => [
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'to' => [
|
'to' => [
|
||||||
|
|||||||
@ -30,7 +30,7 @@ class SendInvoiceRequest extends FormRequest
|
|||||||
'subject' => [
|
'subject' => [
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'mail_sender_id' => [
|
'from' => [
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'to' => [
|
'to' => [
|
||||||
|
|||||||
@ -30,7 +30,7 @@ class SendPaymentRequest extends FormRequest
|
|||||||
'body' => [
|
'body' => [
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'mail_sender_id' => [
|
'from' => [
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'to' => [
|
'to' => [
|
||||||
|
|||||||
@ -1,39 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Crater\Http\Requests;
|
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
|
||||||
|
|
||||||
class TestMailDriverRequest extends FormRequest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Determine if the user is authorized to make this request.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function authorize()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the validation rules that apply to the request.
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function rules()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'to' => [
|
|
||||||
'required',
|
|
||||||
'email'
|
|
||||||
],
|
|
||||||
'subject' => [
|
|
||||||
'required'
|
|
||||||
],
|
|
||||||
'message' => [
|
|
||||||
'required'
|
|
||||||
],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,30 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Crater\Http\Resources;
|
|
||||||
|
|
||||||
use Illuminate\Http\Resources\Json\JsonResource;
|
|
||||||
|
|
||||||
class MailSenderResource extends JsonResource
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Transform the resource into an array.
|
|
||||||
*
|
|
||||||
* @param \Illuminate\Http\Request $request
|
|
||||||
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
|
|
||||||
*/
|
|
||||||
public function toArray($request)
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
'id' => $this->id,
|
|
||||||
'name' => $this->name,
|
|
||||||
'driver' => $this->driver,
|
|
||||||
'is_default' => $this->is_default,
|
|
||||||
'bcc' => $this->bcc,
|
|
||||||
'cc' => $this->cc,
|
|
||||||
'from_address' => $this->from_address,
|
|
||||||
'from_name' => $this->from_name,
|
|
||||||
'company_id' => $this->company_id,
|
|
||||||
'settings' => $this->settings
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -30,7 +30,7 @@ class EstimateViewedMail extends Mailable
|
|||||||
*/
|
*/
|
||||||
public function build()
|
public function build()
|
||||||
{
|
{
|
||||||
return $this->from($this->data['from_address'], $this->data['from_name'])
|
return $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||||
->markdown('emails.viewed.estimate', ['data', $this->data]);
|
->markdown('emails.viewed.estimate', ['data', $this->data]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,7 +30,7 @@ class InvoiceViewedMail extends Mailable
|
|||||||
*/
|
*/
|
||||||
public function build()
|
public function build()
|
||||||
{
|
{
|
||||||
return $this->from($this->data['from_address'], $this->data['from_name'])
|
return $this->from(config('mail.from.address'), config('mail.from.name'))
|
||||||
->markdown('emails.viewed.invoice', ['data', $this->data]);
|
->markdown('emails.viewed.invoice', ['data', $this->data]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,7 +34,7 @@ class SendEstimateMail extends Mailable
|
|||||||
public function build()
|
public function build()
|
||||||
{
|
{
|
||||||
$log = EmailLog::create([
|
$log = EmailLog::create([
|
||||||
'from' => $this->data['from_address'],
|
'from' => $this->data['from'],
|
||||||
'to' => $this->data['to'],
|
'to' => $this->data['to'],
|
||||||
'subject' => $this->data['subject'],
|
'subject' => $this->data['subject'],
|
||||||
'body' => $this->data['body'],
|
'body' => $this->data['body'],
|
||||||
@ -47,10 +47,9 @@ class SendEstimateMail extends Mailable
|
|||||||
|
|
||||||
$this->data['url'] = route('estimate', ['email_log' => $log->token]);
|
$this->data['url'] = route('estimate', ['email_log' => $log->token]);
|
||||||
|
|
||||||
$mailContent = $this->from($this->data['from_address'], $this->data['from_name'])
|
$mailContent = $this->from($this->data['from'], config('mail.from.name'))
|
||||||
->subject($this->data['subject'])
|
->subject($this->data['subject'])
|
||||||
->markdown("emails.send.estimate", ['data', $this->data]);
|
->markdown('emails.send.estimate', ['data', $this->data]);
|
||||||
|
|
||||||
|
|
||||||
if ($this->data['attach']['data']) {
|
if ($this->data['attach']['data']) {
|
||||||
$mailContent->attachData(
|
$mailContent->attachData(
|
||||||
|
|||||||
@ -34,7 +34,7 @@ class SendInvoiceMail extends Mailable
|
|||||||
public function build()
|
public function build()
|
||||||
{
|
{
|
||||||
$log = EmailLog::create([
|
$log = EmailLog::create([
|
||||||
'from' => $this->data['from_address'],
|
'from' => $this->data['from'],
|
||||||
'to' => $this->data['to'],
|
'to' => $this->data['to'],
|
||||||
'subject' => $this->data['subject'],
|
'subject' => $this->data['subject'],
|
||||||
'body' => $this->data['body'],
|
'body' => $this->data['body'],
|
||||||
@ -47,9 +47,9 @@ class SendInvoiceMail extends Mailable
|
|||||||
|
|
||||||
$this->data['url'] = route('invoice', ['email_log' => $log->token]);
|
$this->data['url'] = route('invoice', ['email_log' => $log->token]);
|
||||||
|
|
||||||
$mailContent = $this->from($this->data['from_address'], $this->data['from_name'])
|
$mailContent = $this->from($this->data['from'], config('mail.from.name'))
|
||||||
->subject($this->data['subject'])
|
->subject($this->data['subject'])
|
||||||
->markdown("emails.send.invoice", ['data', $this->data]);
|
->markdown('emails.send.invoice', ['data', $this->data]);
|
||||||
|
|
||||||
if ($this->data['attach']['data']) {
|
if ($this->data['attach']['data']) {
|
||||||
$mailContent->attachData(
|
$mailContent->attachData(
|
||||||
|
|||||||
@ -34,7 +34,7 @@ class SendPaymentMail extends Mailable
|
|||||||
public function build()
|
public function build()
|
||||||
{
|
{
|
||||||
$log = EmailLog::create([
|
$log = EmailLog::create([
|
||||||
'from' => $this->data['from_address'],
|
'from' => $this->data['from'],
|
||||||
'to' => $this->data['to'],
|
'to' => $this->data['to'],
|
||||||
'subject' => $this->data['subject'],
|
'subject' => $this->data['subject'],
|
||||||
'body' => $this->data['body'],
|
'body' => $this->data['body'],
|
||||||
@ -47,9 +47,9 @@ class SendPaymentMail extends Mailable
|
|||||||
|
|
||||||
$this->data['url'] = route('payment', ['email_log' => $log->token]);
|
$this->data['url'] = route('payment', ['email_log' => $log->token]);
|
||||||
|
|
||||||
$mailContent = $this->from($this->data['from_address'], $this->data['from_name'])
|
$mailContent = $this->from($this->data['from'], config('mail.from.name'))
|
||||||
->subject($this->data['subject'])
|
->subject($this->data['subject'])
|
||||||
->markdown("emails.send.payment", ['data', $this->data]);
|
->markdown('emails.send.payment', ['data', $this->data]);
|
||||||
|
|
||||||
if ($this->data['attach']['data']) {
|
if ($this->data['attach']['data']) {
|
||||||
$mailContent->attachData(
|
$mailContent->attachData(
|
||||||
|
|||||||
@ -5,10 +5,10 @@ namespace Crater\Models;
|
|||||||
use App;
|
use App;
|
||||||
use Barryvdh\DomPDF\Facade as PDF;
|
use Barryvdh\DomPDF\Facade as PDF;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
|
use Crater\Mail\SendEstimateMail;
|
||||||
use Crater\Services\SerialNumberFormatter;
|
use Crater\Services\SerialNumberFormatter;
|
||||||
use Crater\Traits\GeneratesPdfTrait;
|
use Crater\Traits\GeneratesPdfTrait;
|
||||||
use Crater\Traits\HasCustomFieldsTrait;
|
use Crater\Traits\HasCustomFieldsTrait;
|
||||||
use Crater\Traits\MailTrait;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
@ -20,7 +20,6 @@ use Vinkla\Hashids\Facades\Hashids;
|
|||||||
class Estimate extends Model implements HasMedia
|
class Estimate extends Model implements HasMedia
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use MailTrait;
|
|
||||||
use InteractsWithMedia;
|
use InteractsWithMedia;
|
||||||
use GeneratesPdfTrait;
|
use GeneratesPdfTrait;
|
||||||
use HasCustomFieldsTrait;
|
use HasCustomFieldsTrait;
|
||||||
@ -364,7 +363,7 @@ class Estimate extends Model implements HasMedia
|
|||||||
$this->save();
|
$this->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->setMail('estimate', $data);
|
\Mail::to($data['to'])->send(new SendEstimateMail($data));
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'success' => true,
|
'success' => true,
|
||||||
|
|||||||
@ -9,7 +9,6 @@ use Crater\Mail\SendInvoiceMail;
|
|||||||
use Crater\Services\SerialNumberFormatter;
|
use Crater\Services\SerialNumberFormatter;
|
||||||
use Crater\Traits\GeneratesPdfTrait;
|
use Crater\Traits\GeneratesPdfTrait;
|
||||||
use Crater\Traits\HasCustomFieldsTrait;
|
use Crater\Traits\HasCustomFieldsTrait;
|
||||||
use Crater\Traits\MailTrait;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
@ -22,7 +21,6 @@ use Vinkla\Hashids\Facades\Hashids;
|
|||||||
class Invoice extends Model implements HasMedia
|
class Invoice extends Model implements HasMedia
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use MailTrait;
|
|
||||||
use InteractsWithMedia;
|
use InteractsWithMedia;
|
||||||
use GeneratesPdfTrait;
|
use GeneratesPdfTrait;
|
||||||
use HasCustomFieldsTrait;
|
use HasCustomFieldsTrait;
|
||||||
@ -466,7 +464,7 @@ class Invoice extends Model implements HasMedia
|
|||||||
{
|
{
|
||||||
$data = $this->sendInvoiceData($data);
|
$data = $this->sendInvoiceData($data);
|
||||||
|
|
||||||
$this->setMail('invoice', $data);
|
\Mail::to($data['to'])->send(new SendInvoiceMail($data));
|
||||||
|
|
||||||
if ($this->status == Invoice::STATUS_DRAFT) {
|
if ($this->status == Invoice::STATUS_DRAFT) {
|
||||||
$this->status = Invoice::STATUS_SENT;
|
$this->status = Invoice::STATUS_SENT;
|
||||||
|
|||||||
@ -1,111 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Crater\Models;
|
|
||||||
|
|
||||||
use Crater\Http\Requests\MailSenderRequest;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
|
||||||
use Config;
|
|
||||||
|
|
||||||
class MailSender extends Model
|
|
||||||
{
|
|
||||||
use HasFactory;
|
|
||||||
|
|
||||||
protected $guarded = [
|
|
||||||
'id'
|
|
||||||
];
|
|
||||||
|
|
||||||
protected $casts = [
|
|
||||||
'settings' => 'array',
|
|
||||||
'is_default' => 'boolean'
|
|
||||||
];
|
|
||||||
|
|
||||||
public function company()
|
|
||||||
{
|
|
||||||
return $this->belongsTo(Company::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function scopeWhereOrder($query, $orderByField, $orderBy)
|
|
||||||
{
|
|
||||||
$query->orderBy($orderByField, $orderBy);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function scopeApplyFilters($query, array $filters)
|
|
||||||
{
|
|
||||||
$filters = collect($filters);
|
|
||||||
|
|
||||||
if ($filters->get('orderByField') || $filters->get('orderBy')) {
|
|
||||||
$field = $filters->get('orderByField') ? $filters->get('orderByField') : 'name';
|
|
||||||
$orderBy = $filters->get('orderBy') ? $filters->get('orderBy') : 'desc';
|
|
||||||
$query->whereOrder($field, $orderBy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function scopePaginateData($query, $limit)
|
|
||||||
{
|
|
||||||
if ($limit == 'all') {
|
|
||||||
return $query->get();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $query->paginate($limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function scopeWhereCompany($query)
|
|
||||||
{
|
|
||||||
$query->where('mail_senders.company_id', request()->header('company'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function createFromRequest(MailSenderRequest $request)
|
|
||||||
{
|
|
||||||
$senderMail = self::create($request->getMailSenderPayload());
|
|
||||||
|
|
||||||
if ($request->is_default) {
|
|
||||||
$senderMail->removeOtherDefaultMailSenders($request);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $senderMail;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updateFromRequest(MailSenderRequest $request)
|
|
||||||
{
|
|
||||||
$data = $request->getMailSenderPayload();
|
|
||||||
|
|
||||||
$this->update($data);
|
|
||||||
|
|
||||||
if ($request->is_default) {
|
|
||||||
$this->removeOtherDefaultMailSenders($request);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function setMailConfiguration($id, $check = null)
|
|
||||||
{
|
|
||||||
$mailSender = MailSender::find($id);
|
|
||||||
|
|
||||||
$settings = $mailSender->settings;
|
|
||||||
$settings['driver'] = $mailSender->driver;
|
|
||||||
$settings['from'] = [
|
|
||||||
'address' => $mailSender->from_address,
|
|
||||||
'name' => $mailSender->from_name
|
|
||||||
];
|
|
||||||
$settings['sendmail'] = config('mail.sendmail');
|
|
||||||
$settings['markdown'] = config('mail.markdown');
|
|
||||||
$settings['log_channel'] = config('mail.log_channel');
|
|
||||||
|
|
||||||
Config::set('mail', $settings);
|
|
||||||
|
|
||||||
if ($check) {
|
|
||||||
return $mailSender;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function removeOtherDefaultMailSenders($request) {
|
|
||||||
MailSender::where('company_id', $request->header('company'))
|
|
||||||
->where('is_default', true)
|
|
||||||
->where('id', '<>', $this->id)
|
|
||||||
->update(['is_default' => false]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -5,10 +5,10 @@ namespace Crater\Models;
|
|||||||
use Barryvdh\DomPDF\Facade as PDF;
|
use Barryvdh\DomPDF\Facade as PDF;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Crater\Jobs\GeneratePaymentPdfJob;
|
use Crater\Jobs\GeneratePaymentPdfJob;
|
||||||
|
use Crater\Mail\SendPaymentMail;
|
||||||
use Crater\Services\SerialNumberFormatter;
|
use Crater\Services\SerialNumberFormatter;
|
||||||
use Crater\Traits\GeneratesPdfTrait;
|
use Crater\Traits\GeneratesPdfTrait;
|
||||||
use Crater\Traits\HasCustomFieldsTrait;
|
use Crater\Traits\HasCustomFieldsTrait;
|
||||||
use Crater\Traits\MailTrait;
|
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Spatie\MediaLibrary\HasMedia;
|
use Spatie\MediaLibrary\HasMedia;
|
||||||
@ -18,7 +18,6 @@ use Vinkla\Hashids\Facades\Hashids;
|
|||||||
class Payment extends Model implements HasMedia
|
class Payment extends Model implements HasMedia
|
||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
use MailTrait;
|
|
||||||
use InteractsWithMedia;
|
use InteractsWithMedia;
|
||||||
use GeneratesPdfTrait;
|
use GeneratesPdfTrait;
|
||||||
use HasCustomFieldsTrait;
|
use HasCustomFieldsTrait;
|
||||||
@ -136,7 +135,7 @@ class Payment extends Model implements HasMedia
|
|||||||
{
|
{
|
||||||
$data = $this->sendPaymentData($data);
|
$data = $this->sendPaymentData($data);
|
||||||
|
|
||||||
$this->setMail('payment', $data);
|
\Mail::to($data['to'])->send(new SendPaymentMail($data));
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'success' => true,
|
'success' => true,
|
||||||
|
|||||||
@ -1,123 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Crater\Policies;
|
|
||||||
|
|
||||||
use Crater\Models\MailSender;
|
|
||||||
use Crater\Models\User;
|
|
||||||
use Illuminate\Auth\Access\HandlesAuthorization;
|
|
||||||
use Silber\Bouncer\BouncerFacade;
|
|
||||||
|
|
||||||
class MailSenderPolicy
|
|
||||||
{
|
|
||||||
use HandlesAuthorization;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the user can view any models.
|
|
||||||
*
|
|
||||||
* @param \Crater\Models\User $user
|
|
||||||
* @return \Illuminate\Auth\Access\Response|bool
|
|
||||||
*/
|
|
||||||
public function viewAny(User $user)
|
|
||||||
{
|
|
||||||
if (BouncerFacade::can('view-mail-sender', MailSender::class)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the user can view the model.
|
|
||||||
*
|
|
||||||
* @param \Crater\Models\User $user
|
|
||||||
* @param \Crater\Models\MailSender $mailSender
|
|
||||||
* @return \Illuminate\Auth\Access\Response|bool
|
|
||||||
*/
|
|
||||||
public function view(User $user, MailSender $mailSender)
|
|
||||||
{
|
|
||||||
if (BouncerFacade::can('view-mail-sender', $mailSender) && $user->hasCompany($mailSender->company_id)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the user can create models.
|
|
||||||
*
|
|
||||||
* @param \Crater\Models\User $user
|
|
||||||
* @return \Illuminate\Auth\Access\Response|bool
|
|
||||||
*/
|
|
||||||
public function create(User $user)
|
|
||||||
{
|
|
||||||
if (BouncerFacade::can('create-mail-sender', MailSender::class)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the user can update the model.
|
|
||||||
*
|
|
||||||
* @param \Crater\Models\User $user
|
|
||||||
* @param \Crater\Models\MailSender $mailSender
|
|
||||||
* @return \Illuminate\Auth\Access\Response|bool
|
|
||||||
*/
|
|
||||||
public function update(User $user, MailSender $mailSender)
|
|
||||||
{
|
|
||||||
if (BouncerFacade::can('edit-mail-sender', $mailSender) && $user->hasCompany($mailSender->company_id)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the user can delete the model.
|
|
||||||
*
|
|
||||||
* @param \Crater\Models\User $user
|
|
||||||
* @param \Crater\Models\MailSender $mailSender
|
|
||||||
* @return \Illuminate\Auth\Access\Response|bool
|
|
||||||
*/
|
|
||||||
public function delete(User $user, MailSender $mailSender)
|
|
||||||
{
|
|
||||||
if (BouncerFacade::can('delete-mail-sender', $mailSender) && $user->hasCompany($mailSender->company_id)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the user can restore the model.
|
|
||||||
*
|
|
||||||
* @param \Crater\Models\User $user
|
|
||||||
* @param \Crater\Models\MailSender $mailSender
|
|
||||||
* @return \Illuminate\Auth\Access\Response|bool
|
|
||||||
*/
|
|
||||||
public function restore(User $user, MailSender $mailSender)
|
|
||||||
{
|
|
||||||
if (BouncerFacade::can('delete-mail-sender', $mailSender) && $user->hasCompany($mailSender->company_id)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the user can permanently delete the model.
|
|
||||||
*
|
|
||||||
* @param \Crater\Models\User $user
|
|
||||||
* @param \Crater\Models\MailSender $mailSender
|
|
||||||
* @return \Illuminate\Auth\Access\Response|bool
|
|
||||||
*/
|
|
||||||
public function forceDelete(User $user, MailSender $mailSender)
|
|
||||||
{
|
|
||||||
if (BouncerFacade::can('delete-mail-sender', $mailSender) && $user->hasCompany($mailSender->company_id)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -39,7 +39,6 @@ class AuthServiceProvider extends ServiceProvider
|
|||||||
\Crater\Models\CustomField::class => \Crater\Policies\CustomFieldPolicy::class,
|
\Crater\Models\CustomField::class => \Crater\Policies\CustomFieldPolicy::class,
|
||||||
\Crater\Models\User::class => \Crater\Policies\UserPolicy::class,
|
\Crater\Models\User::class => \Crater\Policies\UserPolicy::class,
|
||||||
\Crater\Models\Item::class => \Crater\Policies\ItemPolicy::class,
|
\Crater\Models\Item::class => \Crater\Policies\ItemPolicy::class,
|
||||||
\Crater\Models\MailSender::class => \Crater\Policies\MailSenderPolicy::class,
|
|
||||||
\Silber\Bouncer\Database\Role::class => \Crater\Policies\RolePolicy::class,
|
\Silber\Bouncer\Database\Role::class => \Crater\Policies\RolePolicy::class,
|
||||||
\Crater\Models\Unit::class => \Crater\Policies\UnitPolicy::class,
|
\Crater\Models\Unit::class => \Crater\Policies\UnitPolicy::class,
|
||||||
\Crater\Models\RecurringInvoice::class => \Crater\Policies\RecurringInvoicePolicy::class,
|
\Crater\Models\RecurringInvoice::class => \Crater\Policies\RecurringInvoicePolicy::class,
|
||||||
|
|||||||
@ -223,6 +223,204 @@ class EnvironmentManager
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save the mail content to the .env file.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function saveMailVariables(MailEnvironmentRequest $request)
|
||||||
|
{
|
||||||
|
$mailData = $this->getMailData($request);
|
||||||
|
|
||||||
|
try {
|
||||||
|
file_put_contents($this->envPath, str_replace(
|
||||||
|
$mailData['old_mail_data'],
|
||||||
|
$mailData['new_mail_data'],
|
||||||
|
file_get_contents($this->envPath)
|
||||||
|
));
|
||||||
|
|
||||||
|
if ($mailData['extra_old_mail_data']) {
|
||||||
|
file_put_contents($this->envPath, str_replace(
|
||||||
|
$mailData['extra_old_mail_data'],
|
||||||
|
$mailData['extra_mail_data'],
|
||||||
|
file_get_contents($this->envPath)
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
file_put_contents(
|
||||||
|
$this->envPath,
|
||||||
|
"\n".$mailData['extra_mail_data'],
|
||||||
|
FILE_APPEND
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return [
|
||||||
|
'error' => 'mail_variables_save_error',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'success' => 'mail_variables_save_successfully',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMailData($request)
|
||||||
|
{
|
||||||
|
$mailFromCredential = "";
|
||||||
|
$extraMailData = "";
|
||||||
|
$extraOldMailData = "";
|
||||||
|
$oldMailData = "";
|
||||||
|
$newMailData = "";
|
||||||
|
|
||||||
|
if (env('MAIL_FROM_ADDRESS') !== null && env('MAIL_FROM_NAME') !== null) {
|
||||||
|
$mailFromCredential =
|
||||||
|
'MAIL_FROM_ADDRESS='.config('mail.from.address')."\n".
|
||||||
|
'MAIL_FROM_NAME="'.config('mail.from.name')."\"\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($request->mail_driver) {
|
||||||
|
case 'smtp':
|
||||||
|
|
||||||
|
$oldMailData =
|
||||||
|
'MAIL_DRIVER='.config('mail.driver')."\n".
|
||||||
|
'MAIL_HOST='.config('mail.host')."\n".
|
||||||
|
'MAIL_PORT='.config('mail.port')."\n".
|
||||||
|
'MAIL_USERNAME='.config('mail.username')."\n".
|
||||||
|
'MAIL_PASSWORD='.config('mail.password')."\n".
|
||||||
|
'MAIL_ENCRYPTION='.config('mail.encryption')."\n\n".
|
||||||
|
$mailFromCredential;
|
||||||
|
|
||||||
|
$newMailData =
|
||||||
|
'MAIL_DRIVER='.$request->mail_driver."\n".
|
||||||
|
'MAIL_HOST='.$request->mail_host."\n".
|
||||||
|
'MAIL_PORT='.$request->mail_port."\n".
|
||||||
|
'MAIL_USERNAME='.$request->mail_username."\n".
|
||||||
|
'MAIL_PASSWORD='.$request->mail_password."\n".
|
||||||
|
'MAIL_ENCRYPTION='.$request->mail_encryption."\n\n".
|
||||||
|
'MAIL_FROM_ADDRESS='.$request->from_mail."\n".
|
||||||
|
'MAIL_FROM_NAME="'.$request->from_name."\"\n\n";
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'mailgun':
|
||||||
|
$oldMailData =
|
||||||
|
'MAIL_DRIVER='.config('mail.driver')."\n".
|
||||||
|
'MAIL_HOST='.config('mail.host')."\n".
|
||||||
|
'MAIL_PORT='.config('mail.port')."\n".
|
||||||
|
'MAIL_USERNAME='.config('mail.username')."\n".
|
||||||
|
'MAIL_PASSWORD='.config('mail.password')."\n".
|
||||||
|
'MAIL_ENCRYPTION='.config('mail.encryption')."\n\n".
|
||||||
|
$mailFromCredential;
|
||||||
|
|
||||||
|
$newMailData =
|
||||||
|
'MAIL_DRIVER='.$request->mail_driver."\n".
|
||||||
|
'MAIL_HOST='.$request->mail_host."\n".
|
||||||
|
'MAIL_PORT='.$request->mail_port."\n".
|
||||||
|
'MAIL_USERNAME='.config('mail.username')."\n".
|
||||||
|
'MAIL_PASSWORD='.config('mail.password')."\n".
|
||||||
|
'MAIL_ENCRYPTION='.$request->mail_encryption."\n\n".
|
||||||
|
'MAIL_FROM_ADDRESS='.$request->from_mail."\n".
|
||||||
|
'MAIL_FROM_NAME="'.$request->from_name."\"\n\n";
|
||||||
|
|
||||||
|
$extraMailData =
|
||||||
|
'MAILGUN_DOMAIN='.$request->mail_mailgun_domain."\n".
|
||||||
|
'MAILGUN_SECRET='.$request->mail_mailgun_secret."\n".
|
||||||
|
'MAILGUN_ENDPOINT='.$request->mail_mailgun_endpoint."\n";
|
||||||
|
|
||||||
|
if (env('MAILGUN_DOMAIN') !== null && env('MAILGUN_SECRET') !== null && env('MAILGUN_ENDPOINT') !== null) {
|
||||||
|
$extraOldMailData =
|
||||||
|
'MAILGUN_DOMAIN='.config('services.mailgun.domain')."\n".
|
||||||
|
'MAILGUN_SECRET='.config('services.mailgun.secret')."\n".
|
||||||
|
'MAILGUN_ENDPOINT='.config('services.mailgun.endpoint')."\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'ses':
|
||||||
|
$oldMailData =
|
||||||
|
'MAIL_DRIVER='.config('mail.driver')."\n".
|
||||||
|
'MAIL_HOST='.config('mail.host')."\n".
|
||||||
|
'MAIL_PORT='.config('mail.port')."\n".
|
||||||
|
'MAIL_USERNAME='.config('mail.username')."\n".
|
||||||
|
'MAIL_PASSWORD='.config('mail.password')."\n".
|
||||||
|
'MAIL_ENCRYPTION='.config('mail.encryption')."\n\n".
|
||||||
|
$mailFromCredential;
|
||||||
|
|
||||||
|
$newMailData =
|
||||||
|
'MAIL_DRIVER='.$request->mail_driver."\n".
|
||||||
|
'MAIL_HOST='.$request->mail_host."\n".
|
||||||
|
'MAIL_PORT='.$request->mail_port."\n".
|
||||||
|
'MAIL_USERNAME='.config('mail.username')."\n".
|
||||||
|
'MAIL_PASSWORD='.config('mail.password')."\n".
|
||||||
|
'MAIL_ENCRYPTION='.$request->mail_encryption."\n\n".
|
||||||
|
'MAIL_FROM_ADDRESS='.$request->from_mail."\n".
|
||||||
|
'MAIL_FROM_NAME="'.$request->from_name."\"\n\n";
|
||||||
|
|
||||||
|
$extraMailData =
|
||||||
|
'SES_KEY='.$request->mail_ses_key."\n".
|
||||||
|
'SES_SECRET='.$request->mail_ses_secret."\n";
|
||||||
|
|
||||||
|
if (env('SES_KEY') !== null && env('SES_SECRET') !== null) {
|
||||||
|
$extraOldMailData =
|
||||||
|
'SES_KEY='.config('services.ses.key')."\n".
|
||||||
|
'SES_SECRET='.config('services.ses.secret')."\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'mail':
|
||||||
|
$oldMailData =
|
||||||
|
'MAIL_DRIVER='.config('mail.driver')."\n".
|
||||||
|
'MAIL_HOST='.config('mail.host')."\n".
|
||||||
|
'MAIL_PORT='.config('mail.port')."\n".
|
||||||
|
'MAIL_USERNAME='.config('mail.username')."\n".
|
||||||
|
'MAIL_PASSWORD='.config('mail.password')."\n".
|
||||||
|
'MAIL_ENCRYPTION='.config('mail.encryption')."\n\n".
|
||||||
|
$mailFromCredential;
|
||||||
|
|
||||||
|
$newMailData =
|
||||||
|
'MAIL_DRIVER='.$request->mail_driver."\n".
|
||||||
|
'MAIL_HOST='.config('mail.host')."\n".
|
||||||
|
'MAIL_PORT='.config('mail.port')."\n".
|
||||||
|
'MAIL_USERNAME='.config('mail.username')."\n".
|
||||||
|
'MAIL_PASSWORD='.config('mail.password')."\n".
|
||||||
|
'MAIL_ENCRYPTION='.config('mail.encryption')."\n\n".
|
||||||
|
'MAIL_FROM_ADDRESS='.$request->from_mail."\n".
|
||||||
|
'MAIL_FROM_NAME="'.$request->from_name."\"\n\n";
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'sendmail':
|
||||||
|
$oldMailData =
|
||||||
|
'MAIL_DRIVER='.config('mail.driver')."\n".
|
||||||
|
'MAIL_HOST='.config('mail.host')."\n".
|
||||||
|
'MAIL_PORT='.config('mail.port')."\n".
|
||||||
|
'MAIL_USERNAME='.config('mail.username')."\n".
|
||||||
|
'MAIL_PASSWORD='.config('mail.password')."\n".
|
||||||
|
'MAIL_ENCRYPTION='.config('mail.encryption')."\n\n".
|
||||||
|
$mailFromCredential;
|
||||||
|
|
||||||
|
$newMailData =
|
||||||
|
'MAIL_DRIVER='.$request->mail_driver."\n".
|
||||||
|
'MAIL_HOST='.config('mail.host')."\n".
|
||||||
|
'MAIL_PORT='.config('mail.port')."\n".
|
||||||
|
'MAIL_USERNAME='.config('mail.username')."\n".
|
||||||
|
'MAIL_PASSWORD='.config('mail.password')."\n".
|
||||||
|
'MAIL_ENCRYPTION='.config('mail.encryption')."\n\n".
|
||||||
|
'MAIL_FROM_ADDRESS='.$request->from_mail."\n".
|
||||||
|
'MAIL_FROM_NAME="'.$request->from_name."\"\n\n";
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'old_mail_data' => $oldMailData,
|
||||||
|
'new_mail_data' => $newMailData,
|
||||||
|
'extra_mail_data' => $extraMailData,
|
||||||
|
'extra_old_mail_data' => $extraOldMailData,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save the disk content to the .env file.
|
* Save the disk content to the .env file.
|
||||||
*
|
*
|
||||||
|
|||||||
@ -5,7 +5,6 @@ use Crater\Models\Currency;
|
|||||||
use Crater\Models\CustomField;
|
use Crater\Models\CustomField;
|
||||||
use Crater\Models\Setting;
|
use Crater\Models\Setting;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Illuminate\Mail\Mailable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get company setting
|
* Get company setting
|
||||||
@ -71,42 +70,6 @@ function set_active($path, $active = 'active')
|
|||||||
return call_user_func_array('Request::is', (array)$path) ? $active : '';
|
return call_user_func_array('Request::is', (array)$path) ? $active : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Send Mail
|
|
||||||
*
|
|
||||||
* @param Mailable $mailable
|
|
||||||
* @param object $mailSender
|
|
||||||
* @return string $to
|
|
||||||
*/
|
|
||||||
function send_mail(Mailable $mailable, object $mailSender = null, string $to)
|
|
||||||
{
|
|
||||||
if ($mailSender->bcc && $mailSender->cc) {
|
|
||||||
\Mail::to($to)
|
|
||||||
->bcc(explode(',', $mailSender->bcc))
|
|
||||||
->cc(explode(',', $mailSender->cc))
|
|
||||||
->send($mailable);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($mailSender->bcc && $mailSender->cc == null) {
|
|
||||||
\Mail::to($to)
|
|
||||||
->bcc(explode(',', $mailSender->bcc))
|
|
||||||
->send($mailable);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($mailSender->bcc == null && $mailSender->cc) {
|
|
||||||
\Mail::to($to)
|
|
||||||
->cc(explode(',', $mailSender->cc))
|
|
||||||
->send($mailable);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($mailSender->bcc == null && $mailSender->cc == null) {
|
|
||||||
\Mail::to($to)
|
|
||||||
->send($mailable);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $path
|
* @param $path
|
||||||
* @return mixed
|
* @return mixed
|
||||||
|
|||||||
@ -1,40 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace Crater\Traits;
|
|
||||||
|
|
||||||
use Crater\Mail\EstimateViewedMail;
|
|
||||||
use Crater\Mail\InvoiceViewedMail;
|
|
||||||
use Crater\Mail\SendEstimateMail;
|
|
||||||
use Crater\Mail\SendInvoiceMail;
|
|
||||||
use Crater\Mail\SendPaymentMail;
|
|
||||||
use Crater\Models\MailSender;
|
|
||||||
|
|
||||||
trait MailTrait
|
|
||||||
{
|
|
||||||
public function setMail($model, $data)
|
|
||||||
{
|
|
||||||
$mailSender = MailSender::setMailConfiguration($data['mail_sender_id'], true);
|
|
||||||
|
|
||||||
$data['from_address'] = $mailSender->from_address;
|
|
||||||
$data['from_name'] = $mailSender->from_name;
|
|
||||||
|
|
||||||
switch ($model) {
|
|
||||||
case 'invoice':
|
|
||||||
send_mail(new SendInvoiceMail($data), $mailSender, $data['to']);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'estimate':
|
|
||||||
send_mail(new SendEstimateMail($data), $mailSender, $data['to']);
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'payment':
|
|
||||||
send_mail(new SendPaymentMail($data), $mailSender, $data['to']);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -38,15 +38,15 @@
|
|||||||
"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",
|
"fzaninotto/faker": "^1.9.1",
|
||||||
"mockery/mockery": "^1.3.1",
|
"mockery/mockery": "^1.3.1",
|
||||||
"nunomaduro/collision": "^5.0",
|
"nunomaduro/collision": "^5.0",
|
||||||
"pestphp/pest": "^1.0",
|
"pestphp/pest": "^1.0",
|
||||||
"pestphp/pest-plugin-faker": "^1.0",
|
"pestphp/pest-plugin-faker": "^1.0",
|
||||||
"pestphp/pest-plugin-laravel": "^1.0",
|
"pestphp/pest-plugin-laravel": "^1.0",
|
||||||
"pestphp/pest-plugin-parallel": "^0.2.1",
|
"pestphp/pest-plugin-parallel": "^0.2.1",
|
||||||
"phpunit/phpunit": "^9.3"
|
"phpunit/phpunit": "^9.0"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
@ -81,14 +81,11 @@
|
|||||||
"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": {
|
||||||
"dont-discover": []
|
"dont-discover": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
2361
composer.lock
generated
2361
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -7,7 +7,6 @@ use Crater\Models\ExchangeRateProvider;
|
|||||||
use Crater\Models\Expense;
|
use Crater\Models\Expense;
|
||||||
use Crater\Models\Invoice;
|
use Crater\Models\Invoice;
|
||||||
use Crater\Models\Item;
|
use Crater\Models\Item;
|
||||||
use Crater\Models\MailSender;
|
|
||||||
use Crater\Models\Note;
|
use Crater\Models\Note;
|
||||||
use Crater\Models\Payment;
|
use Crater\Models\Payment;
|
||||||
use Crater\Models\RecurringInvoice;
|
use Crater\Models\RecurringInvoice;
|
||||||
@ -398,41 +397,6 @@ return [
|
|||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|
||||||
// Mail Sender
|
|
||||||
[
|
|
||||||
"name" => "view mail sender",
|
|
||||||
"ability" => "view-mail-sender",
|
|
||||||
"model" => MailSender::class,
|
|
||||||
'owner_only' => false,
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"name" => "create mail sender",
|
|
||||||
"ability" => "create-mail-sender",
|
|
||||||
"model" => MailSender::class,
|
|
||||||
'owner_only' => false,
|
|
||||||
"depends_on" => [
|
|
||||||
'view-mail-sender',
|
|
||||||
]
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"name" => "edit mail sender",
|
|
||||||
"ability" => "edit-mail-sender",
|
|
||||||
"model" => MailSender::class,
|
|
||||||
'owner_only' => false,
|
|
||||||
"depends_on" => [
|
|
||||||
'view-mail-sender',
|
|
||||||
]
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"name" => "delete mail sender",
|
|
||||||
"ability" => "delete-mail-sender",
|
|
||||||
"model" => MailSender::class,
|
|
||||||
'owner_only' => false,
|
|
||||||
"depends_on" => [
|
|
||||||
'view-mail-sender',
|
|
||||||
]
|
|
||||||
],
|
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
[
|
[
|
||||||
"name" => "view company dashboard",
|
"name" => "view company dashboard",
|
||||||
|
|||||||
@ -7,7 +7,6 @@ use Crater\Models\ExchangeRateProvider;
|
|||||||
use Crater\Models\Expense;
|
use Crater\Models\Expense;
|
||||||
use Crater\Models\Invoice;
|
use Crater\Models\Invoice;
|
||||||
use Crater\Models\Item;
|
use Crater\Models\Item;
|
||||||
use Crater\Models\MailSender;
|
|
||||||
use Crater\Models\Note;
|
use Crater\Models\Note;
|
||||||
use Crater\Models\Payment;
|
use Crater\Models\Payment;
|
||||||
use Crater\Models\RecurringInvoice;
|
use Crater\Models\RecurringInvoice;
|
||||||
@ -72,7 +71,6 @@ return [
|
|||||||
["code" => "cs", "name" => "Czech"],
|
["code" => "cs", "name" => "Czech"],
|
||||||
["code" => "el", "name" => "Greek"],
|
["code" => "el", "name" => "Greek"],
|
||||||
["code" => "hr", "name" => "Crotian"],
|
["code" => "hr", "name" => "Crotian"],
|
||||||
["code" => "th", "name" => "ไทย"],
|
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -226,17 +224,6 @@ return [
|
|||||||
'ability' => 'view-all-notes',
|
'ability' => 'view-all-notes',
|
||||||
'model' => Note::class
|
'model' => Note::class
|
||||||
],
|
],
|
||||||
[
|
|
||||||
'title' => 'settings.menu_title.mail_sender',
|
|
||||||
'group' => '',
|
|
||||||
'name' => 'Mail Sender',
|
|
||||||
'link' => '/admin/settings/mail-sender',
|
|
||||||
'icon' => 'MailIcon',
|
|
||||||
'owner_only' => false,
|
|
||||||
'ability' => 'view-mail-sender',
|
|
||||||
'model' => MailSender::class
|
|
||||||
],
|
|
||||||
|
|
||||||
[
|
[
|
||||||
'title' => 'settings.menu_title.expense_category',
|
'title' => 'settings.menu_title.expense_category',
|
||||||
'group' => '',
|
'group' => '',
|
||||||
@ -247,6 +234,16 @@ return [
|
|||||||
'ability' => 'view-expense',
|
'ability' => 'view-expense',
|
||||||
'model' => Expense::class
|
'model' => Expense::class
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'title' => 'settings.mail.mail_config',
|
||||||
|
'group' => '',
|
||||||
|
'name' => 'Mail Configuration',
|
||||||
|
'link' => '/admin/settings/mail-configuration',
|
||||||
|
'icon' => 'MailIcon',
|
||||||
|
'owner_only' => true,
|
||||||
|
'ability' => '',
|
||||||
|
'model' => ''
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'title' => 'settings.menu_title.file_disk',
|
'title' => 'settings.menu_title.file_disk',
|
||||||
'group' => '',
|
'group' => '',
|
||||||
@ -277,7 +274,6 @@ return [
|
|||||||
'ability' => '',
|
'ability' => '',
|
||||||
'model' => ''
|
'model' => ''
|
||||||
],
|
],
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -27,7 +27,6 @@ return [
|
|||||||
'tokenizer',
|
'tokenizer',
|
||||||
'JSON',
|
'JSON',
|
||||||
'cURL',
|
'cURL',
|
||||||
'zip',
|
|
||||||
],
|
],
|
||||||
'apache' => [
|
'apache' => [
|
||||||
'mod_rewrite',
|
'mod_rewrite',
|
||||||
|
|||||||
@ -1,106 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
use Crater\Models\Company;
|
|
||||||
use Crater\Models\MailSender;
|
|
||||||
use Crater\Models\User;
|
|
||||||
use Illuminate\Database\Migrations\Migration;
|
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
|
||||||
use Illuminate\Support\Facades\Schema;
|
|
||||||
use Silber\Bouncer\BouncerFacade;
|
|
||||||
|
|
||||||
class CreateMailSendersTable extends Migration
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Run the migrations.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function up()
|
|
||||||
{
|
|
||||||
Schema::create('mail_senders', function (Blueprint $table) {
|
|
||||||
$table->id();
|
|
||||||
$table->string('name');
|
|
||||||
$table->string('driver');
|
|
||||||
$table->boolean('is_default')->default(false);
|
|
||||||
$table->string('bcc')->nullable();
|
|
||||||
$table->string('cc')->nullable();
|
|
||||||
$table->string('from_address')->nullable();
|
|
||||||
$table->string('from_name')->nullable();
|
|
||||||
$table->json('settings')->nullable();
|
|
||||||
$table->integer('company_id')->unsigned()->nullable();
|
|
||||||
$table->foreign('company_id')->references('id')->on('companies');
|
|
||||||
$table->timestamps();
|
|
||||||
});
|
|
||||||
|
|
||||||
$users = User::where('role', 'super admin')->get();
|
|
||||||
|
|
||||||
foreach ($users as $user) {
|
|
||||||
BouncerFacade::allow($user)->toManage(MailSender::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
$companies = Company::all();
|
|
||||||
|
|
||||||
$companies->map(function ($company) {
|
|
||||||
if (env('MAIL_DRIVER') == 'smtp') {
|
|
||||||
$settings = [
|
|
||||||
'MAIL_HOST' => env('MAIL_HOST'),
|
|
||||||
'MAIL_PORT' => env('MAIL_PORT'),
|
|
||||||
'MAIL_USERNAME' => env('MAIL_USERNAME'),
|
|
||||||
'MAIL_PASSWORD' => env('MAIL_PASSWORD'),
|
|
||||||
'MAIL_ENCRYPTION' => env('MAIL_ENCRYPTION')
|
|
||||||
];
|
|
||||||
$this->createSender($settings, $company->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env('MAIL_DRIVER') == 'mail' || env('MAIL_DRIVER') == 'sendmail') {
|
|
||||||
$this->createSender(null, $company->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env('MAIL_DRIVER') == 'mailgun') {
|
|
||||||
$settings = [
|
|
||||||
'MAILGUN_DOMAIN' => env('MAILGUN_DOMAIN'),
|
|
||||||
'MAILGUN_SECRET' => env('MAILGUN_SECRET'),
|
|
||||||
'MAILGUN_ENDPOINT' => env('MAILGUN_ENDPOINT'),
|
|
||||||
];
|
|
||||||
$this->createSender($settings, $company->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env('MAIL_DRIVER') == 'ses') {
|
|
||||||
$settings = [
|
|
||||||
'MAIL_HOST' => env('MAIL_HOST'),
|
|
||||||
'MAIL_PORT' => env('MAIL_PORT'),
|
|
||||||
'MAIL_ENCRYPTION' => env('MAIL_ENCRYPTION'),
|
|
||||||
'MAILGUN_DOMAIN' => env('MAILGUN_DOMAIN'),
|
|
||||||
'SES_KEY' => env('SES_KEY'),
|
|
||||||
'SES_SECRET' => env('SES_SECRET'),
|
|
||||||
];
|
|
||||||
$this->createSender($settings, $company->id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function createSender($settings, $company_id)
|
|
||||||
{
|
|
||||||
$data = [
|
|
||||||
'name' => env('MAIL_DRIVER'),
|
|
||||||
'driver' => env('MAIL_DRIVER'),
|
|
||||||
'is_default' => true,
|
|
||||||
'from_address' => env('MAIL_FROM_ADDRESS'),
|
|
||||||
'from_name' => env('MAIL_FROM_NAME'),
|
|
||||||
'settings' => $settings ?? null,
|
|
||||||
'company_id' => $company_id
|
|
||||||
];
|
|
||||||
|
|
||||||
MailSender::create($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reverse the migrations.
|
|
||||||
*
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public function down()
|
|
||||||
{
|
|
||||||
Schema::dropIfExists('mail_senders');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -170,7 +170,7 @@ class CountriesTableSeeder extends Seeder
|
|||||||
['id' => 152,'code' => 'NR','name' => "Nauru",'phonecode' => 674],
|
['id' => 152,'code' => 'NR','name' => "Nauru",'phonecode' => 674],
|
||||||
['id' => 153,'code' => 'NP','name' => "Nepal",'phonecode' => 977],
|
['id' => 153,'code' => 'NP','name' => "Nepal",'phonecode' => 977],
|
||||||
['id' => 154,'code' => 'AN','name' => "Netherlands Antilles",'phonecode' => 599],
|
['id' => 154,'code' => 'AN','name' => "Netherlands Antilles",'phonecode' => 599],
|
||||||
['id' => 155,'code' => 'NL','name' => "Netherlands",'phonecode' => 31],
|
['id' => 155,'code' => 'NL','name' => "Netherlands The",'phonecode' => 31],
|
||||||
['id' => 156,'code' => 'NC','name' => "New Caledonia",'phonecode' => 687],
|
['id' => 156,'code' => 'NC','name' => "New Caledonia",'phonecode' => 687],
|
||||||
['id' => 157,'code' => 'NZ','name' => "New Zealand",'phonecode' => 64],
|
['id' => 157,'code' => 'NZ','name' => "New Zealand",'phonecode' => 64],
|
||||||
['id' => 158,'code' => 'NI','name' => "Nicaragua",'phonecode' => 505],
|
['id' => 158,'code' => 'NI','name' => "Nicaragua",'phonecode' => 505],
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
FROM php:8.0-fpm-alpine
|
FROM php:7.4-fpm-alpine
|
||||||
|
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
php8-bcmath
|
php7-bcmath
|
||||||
|
|
||||||
RUN docker-php-ext-install pdo pdo_mysql bcmath
|
RUN docker-php-ext-install pdo pdo_mysql bcmath
|
||||||
|
|
||||||
|
|||||||
@ -47,6 +47,8 @@ const ExpenseCategory = () =>
|
|||||||
import('@/scripts/admin/views/settings/ExpenseCategorySetting.vue')
|
import('@/scripts/admin/views/settings/ExpenseCategorySetting.vue')
|
||||||
const ExchangeRateSetting = () =>
|
const ExchangeRateSetting = () =>
|
||||||
import('@/scripts/admin/views/settings/ExchangeRateProviderSetting.vue')
|
import('@/scripts/admin/views/settings/ExchangeRateProviderSetting.vue')
|
||||||
|
const MailConfig = () =>
|
||||||
|
import('@/scripts/admin/views/settings/MailConfigSetting.vue')
|
||||||
const FileDisk = () =>
|
const FileDisk = () =>
|
||||||
import('@/scripts/admin/views/settings/FileDiskSetting.vue')
|
import('@/scripts/admin/views/settings/FileDiskSetting.vue')
|
||||||
const Backup = () => import('@/scripts/admin/views/settings/BackupSetting.vue')
|
const Backup = () => import('@/scripts/admin/views/settings/BackupSetting.vue')
|
||||||
@ -54,8 +56,6 @@ const UpdateApp = () =>
|
|||||||
import('@/scripts/admin/views/settings/UpdateAppSetting.vue')
|
import('@/scripts/admin/views/settings/UpdateAppSetting.vue')
|
||||||
const RolesSettings = () =>
|
const RolesSettings = () =>
|
||||||
import('@/scripts/admin/views/settings/RolesSettings.vue')
|
import('@/scripts/admin/views/settings/RolesSettings.vue')
|
||||||
const MailSender = () =>
|
|
||||||
import('@/scripts/admin/views/settings/mail-sender/Index.vue')
|
|
||||||
|
|
||||||
// Items
|
// Items
|
||||||
const ItemsIndex = () => import('@/scripts/admin/views/items/Index.vue')
|
const ItemsIndex = () => import('@/scripts/admin/views/items/Index.vue')
|
||||||
@ -302,6 +302,13 @@ export default [
|
|||||||
meta: { ability: abilities.VIEW_EXPENSE },
|
meta: { ability: abilities.VIEW_EXPENSE },
|
||||||
component: ExpenseCategory,
|
component: ExpenseCategory,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
path: 'mail-configuration',
|
||||||
|
name: 'mailconfig',
|
||||||
|
meta: { isOwner: true },
|
||||||
|
component: MailConfig,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'file-disk',
|
path: 'file-disk',
|
||||||
name: 'file-disk',
|
name: 'file-disk',
|
||||||
@ -320,13 +327,6 @@ export default [
|
|||||||
meta: { isOwner: true },
|
meta: { isOwner: true },
|
||||||
component: UpdateApp,
|
component: UpdateApp,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'mail-sender',
|
|
||||||
name: 'mailsender',
|
|
||||||
meta: { ability: abilities.VIEW_MAIL_SENDER },
|
|
||||||
component: MailSender,
|
|
||||||
},
|
|
||||||
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -1,123 +0,0 @@
|
|||||||
<template>
|
|
||||||
<!-- warning alert -->
|
|
||||||
<div
|
|
||||||
v-if="type == 'warning'"
|
|
||||||
class="rounded-md p-4 m-5"
|
|
||||||
:class="{
|
|
||||||
'bg-yellow-50': type == 'warning',
|
|
||||||
'bg-blue-50': type == 'info',
|
|
||||||
'bg-red-50': type == 'error',
|
|
||||||
'bg-green-50': type == 'success',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<div class="flex">
|
|
||||||
<div class="flex-shrink-0">
|
|
||||||
<!-- Heroicon name: solid/exclamation -->
|
|
||||||
<svg
|
|
||||||
v-if="type == 'warning'"
|
|
||||||
class="h-5 w-5 text-yellow-400"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<!-- Heroicon name: solid/information-circle -->
|
|
||||||
<svg
|
|
||||||
v-else-if="type == 'info'"
|
|
||||||
class="h-5 w-5 text-blue-400"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<!-- Heroicon name: solid/x-circle -->
|
|
||||||
<svg
|
|
||||||
v-else-if="type == 'error'"
|
|
||||||
class="h-5 w-5 text-red-400"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<!-- Heroicon name: solid/check-circle -->
|
|
||||||
<svg
|
|
||||||
v-else
|
|
||||||
class="h-5 w-5 text-green-400"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
fill="currentColor"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fill-rule="evenodd"
|
|
||||||
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
|
|
||||||
clip-rule="evenodd"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
<div class="ml-3">
|
|
||||||
<h3
|
|
||||||
class="text-sm font-medium"
|
|
||||||
:class="{
|
|
||||||
'text-yellow-800': type == 'warning',
|
|
||||||
'text-blue-800': type == 'info',
|
|
||||||
'text-red-800': type == 'error',
|
|
||||||
'text-green-800': type == 'success',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{ title }}
|
|
||||||
</h3>
|
|
||||||
<div
|
|
||||||
class="text-sm"
|
|
||||||
:class="{
|
|
||||||
'text-yellow-700': type == 'warning',
|
|
||||||
'text-blue-700': type == 'info',
|
|
||||||
'text-red-700': type == 'error',
|
|
||||||
'text-green-700': type == 'success',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<p>{{ description }}</p>
|
|
||||||
</div>
|
|
||||||
<slot name="action"></slot>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
const props = defineProps({
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
default: null,
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
default: 'success',
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
@ -1,111 +0,0 @@
|
|||||||
<template>
|
|
||||||
<BaseDropdown>
|
|
||||||
<template #activator>
|
|
||||||
<BaseButton v-if="route.name === 'mailsender.view'" variant="primary">
|
|
||||||
<BaseIcon name="DotsHorizontalIcon" class="h-5 text-white" />
|
|
||||||
</BaseButton>
|
|
||||||
<BaseIcon v-else name="DotsHorizontalIcon" class="h-5 text-gray-500" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- edit mail-sender -->
|
|
||||||
<BaseDropdownItem @click="editMailSender(row.id)">
|
|
||||||
<BaseIcon
|
|
||||||
name="PencilIcon"
|
|
||||||
class="w-5 h-5 mr-3 text-gray-400 group-hover:text-gray-500"
|
|
||||||
/>
|
|
||||||
{{ $t('general.edit') }}
|
|
||||||
</BaseDropdownItem>
|
|
||||||
|
|
||||||
<!-- send test mail-sender -->
|
|
||||||
<BaseDropdownItem @click="openMailSenderTestModal(row.id)">
|
|
||||||
<BaseIcon
|
|
||||||
name="PaperAirplaneIcon"
|
|
||||||
class="w-5 h-5 mr-3 text-gray-400 group-hover:text-gray-500"
|
|
||||||
/>
|
|
||||||
{{ $t('general.send_test_mail') }}
|
|
||||||
</BaseDropdownItem>
|
|
||||||
|
|
||||||
<!-- delete mail-sender -->
|
|
||||||
<BaseDropdownItem v-if="!row.is_default" @click="removeMailSender(row.id)">
|
|
||||||
<BaseIcon
|
|
||||||
name="TrashIcon"
|
|
||||||
class="w-5 h-5 mr-3 text-gray-400 group-hover:text-gray-500"
|
|
||||||
/>
|
|
||||||
{{ $t('general.delete') }}
|
|
||||||
</BaseDropdownItem>
|
|
||||||
</BaseDropdown>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { useDialogStore } from '@/scripts/stores/dialog'
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
import { useMailSenderStore } from '@/scripts/admin/stores/mail-sender'
|
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
|
||||||
import { inject } from 'vue'
|
|
||||||
import { useModalStore } from '@/scripts/stores/modal'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
row: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
table: {
|
|
||||||
type: Object,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
loadData: {
|
|
||||||
type: Function,
|
|
||||||
default: null,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const pre_t = 'settings.mail_sender'
|
|
||||||
const dialogStore = useDialogStore()
|
|
||||||
const { t } = useI18n()
|
|
||||||
const mailSenderStore = useMailSenderStore()
|
|
||||||
const route = useRoute()
|
|
||||||
const modalStore = useModalStore()
|
|
||||||
|
|
||||||
async function editMailSender(id) {
|
|
||||||
await mailSenderStore.fetchMailSender(id)
|
|
||||||
modalStore.openModal({
|
|
||||||
title: t(`${pre_t}.edit_mail_sender`),
|
|
||||||
componentName: 'MailSenderModal',
|
|
||||||
size: 'md',
|
|
||||||
refreshData: props.loadData && props.loadData,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeMailSender(id) {
|
|
||||||
dialogStore
|
|
||||||
.openDialog({
|
|
||||||
title: t('general.are_you_sure'),
|
|
||||||
message: t(`${pre_t}.confirm_delete`),
|
|
||||||
yesLabel: t('general.ok'),
|
|
||||||
noLabel: t('general.cancel'),
|
|
||||||
variant: 'danger',
|
|
||||||
hideNoButton: false,
|
|
||||||
size: 'lg',
|
|
||||||
})
|
|
||||||
.then(async (res) => {
|
|
||||||
if (res) {
|
|
||||||
let response = await mailSenderStore.deleteMailSender(id)
|
|
||||||
if (response.data.success) {
|
|
||||||
props.loadData && props.loadData()
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
props.loadData && props.loadData()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async function openMailSenderTestModal(id) {
|
|
||||||
modalStore.openModal({
|
|
||||||
title: t(`general.send_test_mail`),
|
|
||||||
componentName: 'MailSenderTestModal',
|
|
||||||
size: 'md',
|
|
||||||
id: id,
|
|
||||||
refreshData: props.loadData && props.loadData,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -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>
|
||||||
|
|||||||
@ -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 (localTax.compound_tax && props.discountedTotal) {
|
if (localTax.compound_tax && props.total) {
|
||||||
return ((props.discountedTotal + props.totalTax) * localTax.percent) / 100
|
return ((props.total + props.totalTax) * localTax.percent) / 100
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.discountedTotal && localTax.percent) {
|
if (props.total && localTax.percent) {
|
||||||
return (props.discountedTotal * localTax.percent) / 100
|
return (props.total * localTax.percent) / 100
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.discountedTotal,
|
() => props.total,
|
||||||
() => {
|
() => {
|
||||||
updateRowTax()
|
updateRowTax()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
@ -305,7 +334,6 @@ function selectPercentage() {
|
|||||||
|
|
||||||
function onSelectTax(selectedTax) {
|
function onSelectTax(selectedTax) {
|
||||||
let amount = 0
|
let amount = 0
|
||||||
|
|
||||||
if (selectedTax.compound_tax && props.store.getSubtotalWithDiscount) {
|
if (selectedTax.compound_tax && props.store.getSubtotalWithDiscount) {
|
||||||
amount = Math.round(
|
amount = Math.round(
|
||||||
((props.store.getSubtotalWithDiscount + props.store.getTotalSimpleTax) *
|
((props.store.getSubtotalWithDiscount + props.store.getTotalSimpleTax) *
|
||||||
|
|||||||
@ -453,7 +453,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
@ -549,6 +549,7 @@ const rules = computed(() => {
|
|||||||
website: {
|
website: {
|
||||||
url: helpers.withMessage(t('validation.invalid_url'), url),
|
url: helpers.withMessage(t('validation.invalid_url'), url),
|
||||||
},
|
},
|
||||||
|
|
||||||
billing: {
|
billing: {
|
||||||
address_street_1: {
|
address_street_1: {
|
||||||
maxLength: helpers.withMessage(
|
maxLength: helpers.withMessage(
|
||||||
|
|||||||
@ -1,287 +0,0 @@
|
|||||||
<template>
|
|
||||||
<BaseModal
|
|
||||||
:show="modalStore.active && modalStore.componentName === 'MailSenderModal'"
|
|
||||||
>
|
|
||||||
<template #header>
|
|
||||||
<div class="flex justify-between w-full">
|
|
||||||
{{ modalStore.title }}
|
|
||||||
<BaseIcon
|
|
||||||
name="XIcon"
|
|
||||||
class="h-6 w-6 text-gray-500 cursor-pointer"
|
|
||||||
@click="closeMailSenderModal"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<form action="" @submit.prevent="submitMailSenderData">
|
|
||||||
<div class="p-4 sm:p-6 my-2">
|
|
||||||
<!-- Name -->
|
|
||||||
<BaseInputGrid>
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.name`)"
|
|
||||||
:error="v$.name.$error && v$.name.$errors[0].$message"
|
|
||||||
:help-text="$t(`${pre_t}.name_help`)"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model.trim="mailSenderStore.currentMailSender.name"
|
|
||||||
:invalid="v$.name.$error"
|
|
||||||
type="text"
|
|
||||||
@input="v$.name.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- From Name -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.from_name`)"
|
|
||||||
:error="v$.from_name.$error && v$.from_name.$errors[0].$message"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model="mailSenderStore.currentMailSender.from_name"
|
|
||||||
:invalid="v$.from_name.$error"
|
|
||||||
type="text"
|
|
||||||
@input="v$.from_name.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- From Address -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.from_address`)"
|
|
||||||
:error="
|
|
||||||
v$.from_address.$error && v$.from_address.$errors[0].$message
|
|
||||||
"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model="mailSenderStore.currentMailSender.from_address"
|
|
||||||
:invalid="v$.from_address.$error"
|
|
||||||
type="text"
|
|
||||||
@input="v$.from_address.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- CC -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.cc`)"
|
|
||||||
:error="v$.cc.$error && v$.cc.$errors[0].$message"
|
|
||||||
:help-text="$t(`${pre_t}.email_list`)"
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model="mailSenderStore.currentMailSender.cc"
|
|
||||||
:invalid="v$.cc.$error"
|
|
||||||
type="text"
|
|
||||||
@input="v$.cc.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- BCC -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.bcc`)"
|
|
||||||
:error="v$.bcc.$error && v$.bcc.$errors[0].$message"
|
|
||||||
:help-text="$t(`${pre_t}.email_list`)"
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model="mailSenderStore.currentMailSender.bcc"
|
|
||||||
:invalid="v$.bcc.$error"
|
|
||||||
type="text"
|
|
||||||
@input="v$.bcc.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- Mail Driver -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.driver`)"
|
|
||||||
:error="v$.driver.$error && v$.driver.$errors[0].$message"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<BaseMultiselect
|
|
||||||
v-model="mailSenderStore.currentMailSender.driver"
|
|
||||||
:options="mailSenderStore.mail_drivers"
|
|
||||||
:can-deselect="false"
|
|
||||||
:invalid="v$.driver.$error"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<component
|
|
||||||
:is="loadMailDriver"
|
|
||||||
:mail-sender-store="mailSenderStore"
|
|
||||||
/>
|
|
||||||
</BaseInputGrid>
|
|
||||||
|
|
||||||
<BaseDivider class="mt-4 mb-0" />
|
|
||||||
|
|
||||||
<!-- Is Default? -->
|
|
||||||
<BaseSwitchSection
|
|
||||||
v-if="!mailSenderStore.isDisable"
|
|
||||||
v-model="mailSenderStore.currentMailSender.is_default"
|
|
||||||
:title="$t(`${pre_t}.is_default`)"
|
|
||||||
:description="$t(`${pre_t}.is_default_description`)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="z-0 flex justify-end p-4 border-t border-solid border--200 border-modal-bg"
|
|
||||||
>
|
|
||||||
<BaseButton
|
|
||||||
class="mr-3 text-sm"
|
|
||||||
variant="primary-outline"
|
|
||||||
type="button"
|
|
||||||
@click="closeMailSenderModal"
|
|
||||||
>
|
|
||||||
{{ $t('general.cancel') }}
|
|
||||||
</BaseButton>
|
|
||||||
<BaseButton
|
|
||||||
:loading="isSaving"
|
|
||||||
:disabled="isSaving"
|
|
||||||
variant="primary"
|
|
||||||
type="submit"
|
|
||||||
>
|
|
||||||
<template #left="slotProps">
|
|
||||||
<BaseIcon
|
|
||||||
v-if="!isSaving"
|
|
||||||
name="SaveIcon"
|
|
||||||
:class="slotProps.class"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
{{
|
|
||||||
mailSenderStore.isEdit ? $t('general.update') : $t('general.save')
|
|
||||||
}}
|
|
||||||
</BaseButton>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</BaseModal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { computed, onMounted, ref } from 'vue'
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
import { required, email, minLength, helpers } from '@vuelidate/validators'
|
|
||||||
import { useVuelidate } from '@vuelidate/core'
|
|
||||||
import { useModalStore } from '@/scripts/stores/modal'
|
|
||||||
import { useMailSenderStore } from '@/scripts/admin/stores/mail-sender'
|
|
||||||
import SmtpDriver from '@/scripts/admin/views/settings/mail-sender/SmtpDriver.vue'
|
|
||||||
import MailgunDriver from '@/scripts/admin/views/settings/mail-sender/MailgunDriver.vue'
|
|
||||||
import SesDriver from '@/scripts/admin/views/settings/mail-sender/SesDriver.vue'
|
|
||||||
|
|
||||||
const pre_t = 'settings.mail_sender'
|
|
||||||
const modalStore = useModalStore()
|
|
||||||
const mailSenderStore = useMailSenderStore()
|
|
||||||
const { t } = useI18n()
|
|
||||||
let isSaving = ref(false)
|
|
||||||
|
|
||||||
const loadMailDriver = computed(() => {
|
|
||||||
switch (mailSenderStore.currentMailSender.driver) {
|
|
||||||
case 'smtp':
|
|
||||||
return SmtpDriver
|
|
||||||
case 'mail':
|
|
||||||
return false
|
|
||||||
case 'sendmail':
|
|
||||||
return false
|
|
||||||
case 'mailgun':
|
|
||||||
return MailgunDriver
|
|
||||||
case 'ses':
|
|
||||||
return SesDriver
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// This is multiple email custom validation
|
|
||||||
const multiEmail = (value) => {
|
|
||||||
if (value == '' || value === null) return true
|
|
||||||
const emailRegex =
|
|
||||||
/^(?:[A-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]{2,}(?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$/i
|
|
||||||
|
|
||||||
const emailArr = value.split(',')
|
|
||||||
let isValid = emailArr.every((v) => {
|
|
||||||
return emailRegex.test(v)
|
|
||||||
})
|
|
||||||
return isValid
|
|
||||||
}
|
|
||||||
|
|
||||||
const rules = computed(() => {
|
|
||||||
return {
|
|
||||||
name: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
minLength: helpers.withMessage(
|
|
||||||
t('validation.name_min_length', { count: 3 }),
|
|
||||||
minLength(3)
|
|
||||||
),
|
|
||||||
},
|
|
||||||
from_name: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
},
|
|
||||||
from_address: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
email: helpers.withMessage(t('validation.email_incorrect'), email),
|
|
||||||
},
|
|
||||||
cc: {
|
|
||||||
multiEmail: helpers.withMessage(
|
|
||||||
t('validation.email_incorrect'),
|
|
||||||
multiEmail
|
|
||||||
),
|
|
||||||
},
|
|
||||||
bcc: {
|
|
||||||
multiEmail: helpers.withMessage(
|
|
||||||
t('validation.email_incorrect'),
|
|
||||||
multiEmail
|
|
||||||
),
|
|
||||||
},
|
|
||||||
driver: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const v$ = useVuelidate(
|
|
||||||
rules,
|
|
||||||
computed(() => mailSenderStore.currentMailSender)
|
|
||||||
)
|
|
||||||
|
|
||||||
async function submitMailSenderData() {
|
|
||||||
v$.value.$touch()
|
|
||||||
if (v$.value.$invalid) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const action = mailSenderStore.isEdit
|
|
||||||
? mailSenderStore.updateMailSender
|
|
||||||
: mailSenderStore.addMailSender
|
|
||||||
isSaving.value = true
|
|
||||||
|
|
||||||
var mailDriverConfig = null
|
|
||||||
switch (mailSenderStore.currentMailSender.driver) {
|
|
||||||
case 'smtp':
|
|
||||||
mailDriverConfig = mailSenderStore.smtpConfig
|
|
||||||
break
|
|
||||||
case 'mailgun':
|
|
||||||
mailDriverConfig = mailSenderStore.mailgunConfig
|
|
||||||
break
|
|
||||||
case 'ses':
|
|
||||||
mailDriverConfig = mailSenderStore.sesConfig
|
|
||||||
break
|
|
||||||
}
|
|
||||||
mailSenderStore.currentMailSender.settings = mailDriverConfig
|
|
||||||
|
|
||||||
let res = await action(mailSenderStore.currentMailSender)
|
|
||||||
isSaving.value = false
|
|
||||||
modalStore.refreshData ? modalStore.refreshData(res.data.data) : ''
|
|
||||||
closeMailSenderModal()
|
|
||||||
} catch (err) {
|
|
||||||
isSaving.value = false
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeMailSenderModal() {
|
|
||||||
modalStore.closeModal()
|
|
||||||
setTimeout(() => {
|
|
||||||
mailSenderStore.resetCurrentMailSender()
|
|
||||||
v$.value.$reset()
|
|
||||||
}, 300)
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
await mailSenderStore.fetchMailDrivers()
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
@ -1,208 +0,0 @@
|
|||||||
<template>
|
|
||||||
<BaseModal :show="modalActive" @open="setInitialData">
|
|
||||||
<template #header>
|
|
||||||
<div class="flex justify-between w-full">
|
|
||||||
{{ modalStore.title }}
|
|
||||||
<BaseIcon
|
|
||||||
name="XIcon"
|
|
||||||
class="w-6 h-6 text-gray-500 cursor-pointer"
|
|
||||||
@click="closeTestModal"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<form action="" @submit.prevent="onTestMailSend">
|
|
||||||
<div class="p-4 md:p-8">
|
|
||||||
<BaseInputGrid layout="one-column">
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.title`)"
|
|
||||||
variant="horizontal"
|
|
||||||
:content-loading="isFetchingInitialData"
|
|
||||||
:error="
|
|
||||||
v$.mail_sender_id.$error && v$.mail_sender_id.$errors[0].$message
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<BaseMultiselect
|
|
||||||
v-model="formData.mail_sender_id"
|
|
||||||
:invalid="v$.mail_sender_id.$error"
|
|
||||||
label="name"
|
|
||||||
:options="mailSenderStore.mailSenders"
|
|
||||||
value-prop="id"
|
|
||||||
:can-deselect="false"
|
|
||||||
:can-clear="false"
|
|
||||||
:placeholder="$t(`${pre_t}.select_mail_sender`)"
|
|
||||||
searchable
|
|
||||||
track-by="name"
|
|
||||||
:content-loading="isFetchingInitialData"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t('general.to')"
|
|
||||||
:error="v$.to.$error && v$.to.$errors[0].$message"
|
|
||||||
variant="horizontal"
|
|
||||||
required
|
|
||||||
:content-loading="isFetchingInitialData"
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model="formData.to"
|
|
||||||
type="text"
|
|
||||||
:invalid="v$.to.$error"
|
|
||||||
:content-loading="isFetchingInitialData"
|
|
||||||
@input="v$.to.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t('general.subject')"
|
|
||||||
:error="v$.subject.$error && v$.subject.$errors[0].$message"
|
|
||||||
variant="horizontal"
|
|
||||||
required
|
|
||||||
:content-loading="isFetchingInitialData"
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model="formData.subject"
|
|
||||||
type="text"
|
|
||||||
:invalid="v$.subject.$error"
|
|
||||||
:content-loading="isFetchingInitialData"
|
|
||||||
@input="v$.subject.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t('general.message')"
|
|
||||||
:error="v$.message.$error && v$.message.$errors[0].$message"
|
|
||||||
variant="horizontal"
|
|
||||||
required
|
|
||||||
:content-loading="isFetchingInitialData"
|
|
||||||
>
|
|
||||||
<BaseTextarea
|
|
||||||
v-model="formData.message"
|
|
||||||
rows="4"
|
|
||||||
cols="50"
|
|
||||||
:invalid="v$.message.$error"
|
|
||||||
:content-loading="isFetchingInitialData"
|
|
||||||
@input="v$.message.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
</BaseInputGrid>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
|
|
||||||
>
|
|
||||||
<BaseButton
|
|
||||||
variant="primary-outline"
|
|
||||||
type="button"
|
|
||||||
class="mr-3"
|
|
||||||
:content-loading="isFetchingInitialData"
|
|
||||||
@click="closeTestModal()"
|
|
||||||
>
|
|
||||||
{{ $t('general.cancel') }}
|
|
||||||
</BaseButton>
|
|
||||||
|
|
||||||
<BaseButton
|
|
||||||
:loading="isSaving"
|
|
||||||
variant="primary"
|
|
||||||
type="submit"
|
|
||||||
:content-loading="isFetchingInitialData"
|
|
||||||
>
|
|
||||||
<template #left="slotProps">
|
|
||||||
<BaseIcon
|
|
||||||
v-if="!isSaving"
|
|
||||||
name="PaperAirplaneIcon"
|
|
||||||
:class="slotProps.class"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
{{ $t('general.send') }}
|
|
||||||
</BaseButton>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</BaseModal>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { reactive, ref, computed } from 'vue'
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
import { required, email, maxLength, helpers } from '@vuelidate/validators'
|
|
||||||
import useVuelidate from '@vuelidate/core'
|
|
||||||
import { useModalStore } from '@/scripts/stores/modal'
|
|
||||||
import { useMailSenderStore } from '@/scripts/admin/stores/mail-sender'
|
|
||||||
|
|
||||||
const pre_t = 'settings.mail_sender'
|
|
||||||
const modalStore = useModalStore()
|
|
||||||
const mailSenderStore = useMailSenderStore()
|
|
||||||
const { t } = useI18n()
|
|
||||||
let isSaving = ref(false)
|
|
||||||
let formData = reactive({
|
|
||||||
mail_sender_id: '',
|
|
||||||
to: '',
|
|
||||||
subject: '',
|
|
||||||
message: '',
|
|
||||||
})
|
|
||||||
const isFetchingInitialData = ref(false)
|
|
||||||
|
|
||||||
const modalActive = computed(() => {
|
|
||||||
return modalStore.active && modalStore.componentName === 'MailSenderTestModal'
|
|
||||||
})
|
|
||||||
|
|
||||||
function setInitialData() {
|
|
||||||
isFetchingInitialData.value = true
|
|
||||||
formData.mail_sender_id = modalStore.id
|
|
||||||
setTimeout(() => {
|
|
||||||
isFetchingInitialData.value = false
|
|
||||||
}, 100)
|
|
||||||
}
|
|
||||||
|
|
||||||
const rules = {
|
|
||||||
mail_sender_id: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
},
|
|
||||||
to: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
email: helpers.withMessage(t('validation.email_incorrect'), email),
|
|
||||||
},
|
|
||||||
subject: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
maxLength: helpers.withMessage(
|
|
||||||
t('validation.subject_maxlength'),
|
|
||||||
maxLength(100)
|
|
||||||
),
|
|
||||||
},
|
|
||||||
message: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
maxLength: helpers.withMessage(
|
|
||||||
t('validation.message_maxlength'),
|
|
||||||
maxLength(255)
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const v$ = useVuelidate(rules, formData)
|
|
||||||
|
|
||||||
function resetFormData() {
|
|
||||||
formData.mail_sender_id = ''
|
|
||||||
formData.to = ''
|
|
||||||
formData.subject = ''
|
|
||||||
formData.message = ''
|
|
||||||
|
|
||||||
v$.value.$reset()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function onTestMailSend() {
|
|
||||||
v$.value.$touch()
|
|
||||||
if (v$.value.$invalid) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
isSaving.value = true
|
|
||||||
let response = await mailSenderStore.sendTestMail(formData)
|
|
||||||
if (response.data) {
|
|
||||||
closeTestModal()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function closeTestModal() {
|
|
||||||
modalStore.closeModal()
|
|
||||||
setTimeout(() => {
|
|
||||||
isSaving.value = false
|
|
||||||
modalStore.resetModalData()
|
|
||||||
resetFormData()
|
|
||||||
}, 300)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -16,28 +16,18 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<form v-if="!isPreview" action="">
|
<form v-if="!isPreview" action="">
|
||||||
<!-- v-if -->
|
<div class="px-8 py-8 sm:p-6">
|
||||||
<div v-if="isMailSenderExist" class="px-8 py-8 sm:p-6">
|
|
||||||
<BaseInputGrid layout="one-column">
|
<BaseInputGrid layout="one-column">
|
||||||
<BaseInputGroup
|
<BaseInputGroup
|
||||||
:label="$tc('settings.mail_sender.title', 1)"
|
:label="$t('general.from')"
|
||||||
required
|
required
|
||||||
:error="
|
:error="v$.from.$error && v$.from.$errors[0].$message"
|
||||||
v$.mail_sender_id.$error && v$.mail_sender_id.$errors[0].$message
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<BaseMultiselect
|
<BaseInput
|
||||||
v-model="estimateMailForm.mail_sender_id"
|
v-model="estimateMailForm.from"
|
||||||
:invalid="v$.mail_sender_id.$error"
|
type="text"
|
||||||
label="name"
|
:invalid="v$.from.$error"
|
||||||
:options="mailSenders"
|
@input="v$.from.$touch()"
|
||||||
value-prop="id"
|
|
||||||
:can-deselect="false"
|
|
||||||
:can-clear="false"
|
|
||||||
:placeholder="$t(`settings.mail_sender.select_mail_sender`)"
|
|
||||||
searchable
|
|
||||||
track-by="name"
|
|
||||||
:content-loading="isFetchingInitialData"
|
|
||||||
/>
|
/>
|
||||||
</BaseInputGroup>
|
</BaseInputGroup>
|
||||||
<BaseInputGroup
|
<BaseInputGroup
|
||||||
@ -72,45 +62,6 @@
|
|||||||
</BaseInputGroup>
|
</BaseInputGroup>
|
||||||
</BaseInputGrid>
|
</BaseInputGrid>
|
||||||
</div>
|
</div>
|
||||||
<!-- v-else -->
|
|
||||||
<div v-else-if="!isMailSenderExist && !isFetchingInitialData">
|
|
||||||
<FeedbackAlert
|
|
||||||
:title="$t('settings.mail_sender.no_mail_sender_found')"
|
|
||||||
:description="
|
|
||||||
$t('settings.mail_sender.no_mail_sender_found_description')
|
|
||||||
"
|
|
||||||
type="warning"
|
|
||||||
>
|
|
||||||
<template #action>
|
|
||||||
<div class="mt-4">
|
|
||||||
<div class="-mx-2 -my-1.5 flex">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="
|
|
||||||
bg-yellow-50
|
|
||||||
px-2
|
|
||||||
py-1.5
|
|
||||||
rounded-md
|
|
||||||
text-sm
|
|
||||||
font-medium
|
|
||||||
text-yellow-800
|
|
||||||
hover:bg-yellow-100
|
|
||||||
focus:outline-none
|
|
||||||
focus:ring-2
|
|
||||||
focus:ring-offset-2
|
|
||||||
focus:ring-offset-yellow-50
|
|
||||||
focus:ring-yellow-600
|
|
||||||
"
|
|
||||||
@click="gotoMailSender"
|
|
||||||
>
|
|
||||||
{{ $t('settings.mail_sender.manage_mail_sender') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</FeedbackAlert>
|
|
||||||
</div>
|
|
||||||
<!-- end v-if-else -->
|
|
||||||
<div
|
<div
|
||||||
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
|
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
|
||||||
>
|
>
|
||||||
@ -124,7 +75,6 @@
|
|||||||
</BaseButton>
|
</BaseButton>
|
||||||
|
|
||||||
<BaseButton
|
<BaseButton
|
||||||
v-if="isMailSenderExist"
|
|
||||||
:loading="isLoading"
|
:loading="isLoading"
|
||||||
:disabled="isLoading"
|
:disabled="isLoading"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
@ -191,24 +141,18 @@ import { useModalStore } from '@/scripts/stores/modal'
|
|||||||
import { useEstimateStore } from '@/scripts/admin/stores/estimate'
|
import { useEstimateStore } from '@/scripts/admin/stores/estimate'
|
||||||
import { useNotificationStore } from '@/scripts/stores/notification'
|
import { useNotificationStore } from '@/scripts/stores/notification'
|
||||||
import { useCompanyStore } from '@/scripts/admin/stores/company'
|
import { useCompanyStore } from '@/scripts/admin/stores/company'
|
||||||
import { useMailSenderStore } from '@/scripts/admin/stores/mail-sender'
|
import { useMailDriverStore } from '@/scripts/admin/stores/mail-driver'
|
||||||
import FeedbackAlert from '@/scripts/admin/components/FeedbackAlert.vue'
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
|
|
||||||
const modalStore = useModalStore()
|
const modalStore = useModalStore()
|
||||||
const estimateStore = useEstimateStore()
|
const estimateStore = useEstimateStore()
|
||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
const companyStore = useCompanyStore()
|
const companyStore = useCompanyStore()
|
||||||
const mailSenderStore = useMailSenderStore()
|
const mailDriverStore = useMailDriverStore()
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
const templateUrl = ref('')
|
const templateUrl = ref('')
|
||||||
const isPreview = ref(false)
|
const isPreview = ref(false)
|
||||||
const mailSenders = ref(null)
|
|
||||||
const isFetchingInitialData = ref(false)
|
|
||||||
const emailTemplates = ref(null)
|
|
||||||
|
|
||||||
const estimateMailFields = ref([
|
const estimateMailFields = ref([
|
||||||
'customer',
|
'customer',
|
||||||
@ -220,7 +164,7 @@ const estimateMailFields = ref([
|
|||||||
|
|
||||||
let estimateMailForm = reactive({
|
let estimateMailForm = reactive({
|
||||||
id: null,
|
id: null,
|
||||||
mail_sender_id: null,
|
from: null,
|
||||||
to: null,
|
to: null,
|
||||||
subject: 'New Estimate',
|
subject: 'New Estimate',
|
||||||
body: null,
|
body: null,
|
||||||
@ -237,8 +181,9 @@ const modalData = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
mail_sender_id: {
|
from: {
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
email: helpers.withMessage(t('validation.email_incorrect'), email),
|
||||||
},
|
},
|
||||||
to: {
|
to: {
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
@ -262,26 +207,20 @@ function cancelPreview() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function setInitialData() {
|
async function setInitialData() {
|
||||||
|
let admin = await companyStore.fetchBasicMailConfig()
|
||||||
|
|
||||||
estimateMailForm.id = modalStore.id
|
estimateMailForm.id = modalStore.id
|
||||||
|
|
||||||
|
if (admin.data) {
|
||||||
|
estimateMailForm.from = admin.data.from_mail
|
||||||
|
}
|
||||||
|
|
||||||
if (modalData.value) {
|
if (modalData.value) {
|
||||||
estimateMailForm.to = modalData.value.customer.email
|
estimateMailForm.to = modalData.value.customer.email
|
||||||
}
|
}
|
||||||
|
|
||||||
estimateMailForm.body =
|
estimateMailForm.body =
|
||||||
companyStore.selectedCompanySettings.estimate_mail_body
|
companyStore.selectedCompanySettings.estimate_mail_body
|
||||||
|
|
||||||
isFetchingInitialData.value = true
|
|
||||||
let mailSenderData = await mailSenderStore.fetchMailSenders({ limit: 'all' })
|
|
||||||
if (mailSenderData.data) {
|
|
||||||
mailSenders.value = mailSenderData.data.data
|
|
||||||
let defaultMailSender = mailSenderData.data.data.find(
|
|
||||||
(mailSender) => mailSender.is_default == true
|
|
||||||
)
|
|
||||||
estimateMailForm.mail_sender_id = defaultMailSender
|
|
||||||
? defaultMailSender.id
|
|
||||||
: null
|
|
||||||
isFetchingInitialData.value = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitForm() {
|
async function submitForm() {
|
||||||
@ -335,18 +274,4 @@ function closeSendEstimateModal() {
|
|||||||
templateUrl.value = null
|
templateUrl.value = null
|
||||||
}, 300)
|
}, 300)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTickImage() {
|
|
||||||
const imgUrl = new URL('/img/tick.png', import.meta.url)
|
|
||||||
return imgUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
const isMailSenderExist = computed(() => {
|
|
||||||
return mailSenders.value && mailSenders.value.length
|
|
||||||
})
|
|
||||||
|
|
||||||
function gotoMailSender() {
|
|
||||||
closeSendEstimateModal()
|
|
||||||
router.push('/admin/settings/mail-sender')
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -15,28 +15,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<form v-if="!isPreview" action="">
|
<form v-if="!isPreview" action="">
|
||||||
<!-- v-if -->
|
<div class="px-8 py-8 sm:p-6">
|
||||||
<div v-if="isMailSenderExist" class="px-8 py-8 sm:p-6">
|
|
||||||
<BaseInputGrid layout="one-column" class="col-span-7">
|
<BaseInputGrid layout="one-column" class="col-span-7">
|
||||||
<BaseInputGroup
|
<BaseInputGroup
|
||||||
:label="$tc('settings.mail_sender.title', 1)"
|
:label="$t('general.from')"
|
||||||
required
|
required
|
||||||
:error="
|
:error="v$.from.$error && v$.from.$errors[0].$message"
|
||||||
v$.mail_sender_id.$error && v$.mail_sender_id.$errors[0].$message
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<BaseMultiselect
|
<BaseInput
|
||||||
v-model="invoiceMailForm.mail_sender_id"
|
v-model="invoiceMailForm.from"
|
||||||
:invalid="v$.mail_sender_id.$error"
|
type="text"
|
||||||
label="name"
|
:invalid="v$.from.$error"
|
||||||
:options="mailSenders"
|
@input="v$.from.$touch()"
|
||||||
value-prop="id"
|
|
||||||
:can-deselect="false"
|
|
||||||
:can-clear="false"
|
|
||||||
:placeholder="$t(`settings.mail_sender.select_mail_sender`)"
|
|
||||||
searchable
|
|
||||||
track-by="name"
|
|
||||||
:content-loading="isFetchingInitialData"
|
|
||||||
/>
|
/>
|
||||||
</BaseInputGroup>
|
</BaseInputGroup>
|
||||||
<BaseInputGroup
|
<BaseInputGroup
|
||||||
@ -75,45 +65,6 @@
|
|||||||
</BaseInputGroup>
|
</BaseInputGroup>
|
||||||
</BaseInputGrid>
|
</BaseInputGrid>
|
||||||
</div>
|
</div>
|
||||||
<!-- v-else -->
|
|
||||||
<div v-else-if="!isMailSenderExist && !isFetchingInitialData">
|
|
||||||
<FeedbackAlert
|
|
||||||
:title="$t('settings.mail_sender.no_mail_sender_found')"
|
|
||||||
:description="
|
|
||||||
$t('settings.mail_sender.no_mail_sender_found_description')
|
|
||||||
"
|
|
||||||
type="warning"
|
|
||||||
>
|
|
||||||
<template #action>
|
|
||||||
<div class="mt-4">
|
|
||||||
<div class="-mx-2 -my-1.5 flex">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="
|
|
||||||
bg-yellow-50
|
|
||||||
px-2
|
|
||||||
py-1.5
|
|
||||||
rounded-md
|
|
||||||
text-sm
|
|
||||||
font-medium
|
|
||||||
text-yellow-800
|
|
||||||
hover:bg-yellow-100
|
|
||||||
focus:outline-none
|
|
||||||
focus:ring-2
|
|
||||||
focus:ring-offset-2
|
|
||||||
focus:ring-offset-yellow-50
|
|
||||||
focus:ring-yellow-600
|
|
||||||
"
|
|
||||||
@click="gotoMailSender"
|
|
||||||
>
|
|
||||||
{{ $t('settings.mail_sender.manage_mail_sender') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</FeedbackAlert>
|
|
||||||
</div>
|
|
||||||
<!-- end v-if-else -->
|
|
||||||
<div
|
<div
|
||||||
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
|
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
|
||||||
>
|
>
|
||||||
@ -126,7 +77,6 @@
|
|||||||
{{ $t('general.cancel') }}
|
{{ $t('general.cancel') }}
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
v-if="isMailSenderExist"
|
|
||||||
:loading="isLoading"
|
:loading="isLoading"
|
||||||
:disabled="isLoading"
|
:disabled="isLoading"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
@ -204,24 +154,18 @@ import { useI18n } from 'vue-i18n'
|
|||||||
import { useInvoiceStore } from '@/scripts/admin/stores/invoice'
|
import { useInvoiceStore } from '@/scripts/admin/stores/invoice'
|
||||||
import { useVuelidate } from '@vuelidate/core'
|
import { useVuelidate } from '@vuelidate/core'
|
||||||
import { required, email, helpers } from '@vuelidate/validators'
|
import { required, email, helpers } from '@vuelidate/validators'
|
||||||
import { useMailSenderStore } from '@/scripts/admin/stores/mail-sender'
|
import { useMailDriverStore } from '@/scripts/admin/stores/mail-driver'
|
||||||
import FeedbackAlert from '@/scripts/admin/components/FeedbackAlert.vue'
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
|
|
||||||
const modalStore = useModalStore()
|
const modalStore = useModalStore()
|
||||||
const companyStore = useCompanyStore()
|
const companyStore = useCompanyStore()
|
||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
const invoiceStore = useInvoiceStore()
|
const invoiceStore = useInvoiceStore()
|
||||||
const mailSenderStore = useMailSenderStore()
|
const mailDriverStore = useMailDriverStore()
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
let isLoading = ref(false)
|
let isLoading = ref(false)
|
||||||
const templateUrl = ref('')
|
const templateUrl = ref('')
|
||||||
const isPreview = ref(false)
|
const isPreview = ref(false)
|
||||||
const mailSenders = ref(null)
|
|
||||||
const isFetchingInitialData = ref(false)
|
|
||||||
const emailTemplates = ref(null)
|
|
||||||
|
|
||||||
const emit = defineEmits(['update'])
|
const emit = defineEmits(['update'])
|
||||||
|
|
||||||
@ -235,7 +179,7 @@ const invoiceMailFields = ref([
|
|||||||
|
|
||||||
const invoiceMailForm = reactive({
|
const invoiceMailForm = reactive({
|
||||||
id: null,
|
id: null,
|
||||||
mail_sender_id: null,
|
from: null,
|
||||||
to: null,
|
to: null,
|
||||||
subject: 'New Invoice',
|
subject: 'New Invoice',
|
||||||
body: null,
|
body: null,
|
||||||
@ -254,8 +198,9 @@ const modalData = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
mail_sender_id: {
|
from: {
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
email: helpers.withMessage(t('validation.email_incorrect'), email),
|
||||||
},
|
},
|
||||||
to: {
|
to: {
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
@ -279,25 +224,19 @@ function cancelPreview() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function setInitialData() {
|
async function setInitialData() {
|
||||||
|
let admin = await companyStore.fetchBasicMailConfig()
|
||||||
|
|
||||||
invoiceMailForm.id = modalStore.id
|
invoiceMailForm.id = modalStore.id
|
||||||
|
|
||||||
|
if (admin.data) {
|
||||||
|
invoiceMailForm.from = admin.data.from_mail
|
||||||
|
}
|
||||||
|
|
||||||
if (modalData.value) {
|
if (modalData.value) {
|
||||||
invoiceMailForm.to = modalData.value.customer.email
|
invoiceMailForm.to = modalData.value.customer.email
|
||||||
}
|
}
|
||||||
|
|
||||||
invoiceMailForm.body = companyStore.selectedCompanySettings.invoice_mail_body
|
invoiceMailForm.body = companyStore.selectedCompanySettings.invoice_mail_body
|
||||||
|
|
||||||
isFetchingInitialData.value = true
|
|
||||||
let mailSenderData = await mailSenderStore.fetchMailSenders({ limit: 'all' })
|
|
||||||
if (mailSenderData.data) {
|
|
||||||
mailSenders.value = mailSenderData.data.data
|
|
||||||
let defaultMailSender = mailSenderData.data.data.find(
|
|
||||||
(mailSender) => mailSender.is_default == true
|
|
||||||
)
|
|
||||||
invoiceMailForm.mail_sender_id = defaultMailSender
|
|
||||||
? defaultMailSender.id
|
|
||||||
: null
|
|
||||||
isFetchingInitialData.value = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function submitForm() {
|
async function submitForm() {
|
||||||
@ -348,18 +287,4 @@ function closeSendInvoiceModal() {
|
|||||||
templateUrl.value = null
|
templateUrl.value = null
|
||||||
}, 300)
|
}, 300)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTickImage() {
|
|
||||||
const imgUrl = new URL('/img/tick.png', import.meta.url)
|
|
||||||
return imgUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
const isMailSenderExist = computed(() => {
|
|
||||||
return mailSenders.value && mailSenders.value.length
|
|
||||||
})
|
|
||||||
|
|
||||||
function gotoMailSender() {
|
|
||||||
closeSendInvoiceModal()
|
|
||||||
router.push('/admin/settings/mail-sender')
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -15,28 +15,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<form v-if="!isPreview" action="">
|
<form v-if="!isPreview" action="">
|
||||||
<!-- v-if -->
|
<div class="px-8 py-8 sm:p-6">
|
||||||
<div v-if="isMailSenderExist" class="px-8 py-8 sm:p-6">
|
|
||||||
<BaseInputGrid layout="one-column" class="col-span-7">
|
<BaseInputGrid layout="one-column" class="col-span-7">
|
||||||
<BaseInputGroup
|
<BaseInputGroup
|
||||||
:label="$tc('settings.mail_sender.title', 1)"
|
:label="$t('general.from')"
|
||||||
required
|
required
|
||||||
:error="
|
:error="v$.from.$error && v$.from.$errors[0].$message"
|
||||||
v$.mail_sender_id.$error && v$.mail_sender_id.$errors[0].$message
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<BaseMultiselect
|
<BaseInput
|
||||||
v-model="paymentMailForm.mail_sender_id"
|
v-model="paymentMailForm.from"
|
||||||
:invalid="v$.mail_sender_id.$error"
|
type="text"
|
||||||
label="name"
|
:invalid="v$.from.$error"
|
||||||
:options="mailSenders"
|
@input="v$.from.$touch()"
|
||||||
value-prop="id"
|
|
||||||
:can-deselect="false"
|
|
||||||
:can-clear="false"
|
|
||||||
:placeholder="$t(`settings.mail_sender.select_mail_sender`)"
|
|
||||||
searchable
|
|
||||||
track-by="name"
|
|
||||||
:content-loading="isFetchingInitialData"
|
|
||||||
/>
|
/>
|
||||||
</BaseInputGroup>
|
</BaseInputGroup>
|
||||||
<BaseInputGroup
|
<BaseInputGroup
|
||||||
@ -75,45 +65,6 @@
|
|||||||
</BaseInputGroup>
|
</BaseInputGroup>
|
||||||
</BaseInputGrid>
|
</BaseInputGrid>
|
||||||
</div>
|
</div>
|
||||||
<!-- v-else -->
|
|
||||||
<div v-else-if="!isMailSenderExist && !isFetchingInitialData">
|
|
||||||
<FeedbackAlert
|
|
||||||
:title="$t('settings.mail_sender.no_mail_sender_found')"
|
|
||||||
:description="
|
|
||||||
$t('settings.mail_sender.no_mail_sender_found_description')
|
|
||||||
"
|
|
||||||
type="warning"
|
|
||||||
>
|
|
||||||
<template #action>
|
|
||||||
<div class="mt-4">
|
|
||||||
<div class="-mx-2 -my-1.5 flex">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="
|
|
||||||
bg-yellow-50
|
|
||||||
px-2
|
|
||||||
py-1.5
|
|
||||||
rounded-md
|
|
||||||
text-sm
|
|
||||||
font-medium
|
|
||||||
text-yellow-800
|
|
||||||
hover:bg-yellow-100
|
|
||||||
focus:outline-none
|
|
||||||
focus:ring-2
|
|
||||||
focus:ring-offset-2
|
|
||||||
focus:ring-offset-yellow-50
|
|
||||||
focus:ring-yellow-600
|
|
||||||
"
|
|
||||||
@click="gotoMailSender"
|
|
||||||
>
|
|
||||||
{{ $t('settings.mail_sender.manage_mail_sender') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</FeedbackAlert>
|
|
||||||
</div>
|
|
||||||
<!-- end v-if-else -->
|
|
||||||
<div
|
<div
|
||||||
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
|
class="z-0 flex justify-end p-4 border-t border-gray-200 border-solid"
|
||||||
>
|
>
|
||||||
@ -126,7 +77,6 @@
|
|||||||
{{ $t('general.cancel') }}
|
{{ $t('general.cancel') }}
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
v-if="isMailSenderExist"
|
|
||||||
:loading="isLoading"
|
:loading="isLoading"
|
||||||
:disabled="isLoading"
|
:disabled="isLoading"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
@ -204,26 +154,20 @@ import { usePaymentStore } from '@/scripts/admin/stores/payment'
|
|||||||
import { useCompanyStore } from '@/scripts/admin/stores/company'
|
import { useCompanyStore } from '@/scripts/admin/stores/company'
|
||||||
import { useNotificationStore } from '@/scripts/stores/notification'
|
import { useNotificationStore } from '@/scripts/stores/notification'
|
||||||
import { useModalStore } from '@/scripts/stores/modal'
|
import { useModalStore } from '@/scripts/stores/modal'
|
||||||
|
import { useMailDriverStore } from '@/scripts/admin/stores/mail-driver'
|
||||||
import { useDialogStore } from '@/scripts/stores/dialog'
|
import { useDialogStore } from '@/scripts/stores/dialog'
|
||||||
import { useMailSenderStore } from '@/scripts/admin/stores/mail-sender'
|
|
||||||
import FeedbackAlert from '@/scripts/admin/components/FeedbackAlert.vue'
|
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
|
|
||||||
const paymentStore = usePaymentStore()
|
const paymentStore = usePaymentStore()
|
||||||
const companyStore = useCompanyStore()
|
const companyStore = useCompanyStore()
|
||||||
const modalStore = useModalStore()
|
const modalStore = useModalStore()
|
||||||
const notificationStore = useNotificationStore()
|
const notificationStore = useNotificationStore()
|
||||||
|
const mailDriversStore = useMailDriverStore()
|
||||||
const dialogStore = useDialogStore()
|
const dialogStore = useDialogStore()
|
||||||
const mailSenderStore = useMailSenderStore()
|
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
let isLoading = ref(false)
|
let isLoading = ref(false)
|
||||||
const templateUrl = ref('')
|
const templateUrl = ref('')
|
||||||
const isPreview = ref(false)
|
const isPreview = ref(false)
|
||||||
const mailSenders = ref(null)
|
|
||||||
const isFetchingInitialData = ref(false)
|
|
||||||
const emailTemplates = ref(null)
|
|
||||||
|
|
||||||
const paymentMailFields = ref([
|
const paymentMailFields = ref([
|
||||||
'customer',
|
'customer',
|
||||||
@ -235,7 +179,7 @@ const paymentMailFields = ref([
|
|||||||
|
|
||||||
const paymentMailForm = reactive({
|
const paymentMailForm = reactive({
|
||||||
id: null,
|
id: null,
|
||||||
mail_sender_id: null,
|
from: null,
|
||||||
to: null,
|
to: null,
|
||||||
subject: 'New Payment',
|
subject: 'New Payment',
|
||||||
body: null,
|
body: null,
|
||||||
@ -254,8 +198,9 @@ const modalData = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
mail_sender_id: {
|
from: {
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
email: helpers.withMessage(t('validation.email_incorrect'), email),
|
||||||
},
|
},
|
||||||
to: {
|
to: {
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
@ -276,25 +221,18 @@ function cancelPreview() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function setInitialData() {
|
async function setInitialData() {
|
||||||
|
let admin = await companyStore.fetchBasicMailConfig()
|
||||||
paymentMailForm.id = modalStore.id
|
paymentMailForm.id = modalStore.id
|
||||||
|
|
||||||
|
if (admin.data) {
|
||||||
|
paymentMailForm.from = admin.data.from_mail
|
||||||
|
}
|
||||||
|
|
||||||
if (modalData.value) {
|
if (modalData.value) {
|
||||||
paymentMailForm.to = modalData.value.customer.email
|
paymentMailForm.to = modalData.value.customer.email
|
||||||
}
|
}
|
||||||
|
|
||||||
paymentMailForm.body = companyStore.selectedCompanySettings.payment_mail_body
|
paymentMailForm.body = companyStore.selectedCompanySettings.payment_mail_body
|
||||||
|
|
||||||
isFetchingInitialData.value = true
|
|
||||||
let mailSenderData = await mailSenderStore.fetchMailSenders({ limit: 'all' })
|
|
||||||
if (mailSenderData.data) {
|
|
||||||
mailSenders.value = mailSenderData.data.data
|
|
||||||
let defaultMailSender = mailSenderData.data.data.find(
|
|
||||||
(mailSender) => mailSender.is_default == true
|
|
||||||
)
|
|
||||||
paymentMailForm.mail_sender_id = defaultMailSender
|
|
||||||
? defaultMailSender.id
|
|
||||||
: null
|
|
||||||
isFetchingInitialData.value = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendPaymentData() {
|
async function sendPaymentData() {
|
||||||
@ -342,18 +280,4 @@ function closeSendPaymentModal() {
|
|||||||
modalStore.resetModalData()
|
modalStore.resetModalData()
|
||||||
}, 300)
|
}, 300)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTickImage() {
|
|
||||||
const imgUrl = new URL('/img/tick.png', import.meta.url)
|
|
||||||
return imgUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
const isMailSenderExist = computed(() => {
|
|
||||||
return mailSenders.value && mailSenders.value.length
|
|
||||||
})
|
|
||||||
|
|
||||||
function gotoMailSender() {
|
|
||||||
closeSendPaymentModal()
|
|
||||||
router.push('/admin/settings/mail-sender')
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|
||||||
|
|||||||
146
resources/scripts/admin/stores/mail-driver.js
Normal file
146
resources/scripts/admin/stores/mail-driver.js
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { useNotificationStore } from '@/scripts/stores/notification'
|
||||||
|
import { handleError } from '@/scripts/helpers/error-handling'
|
||||||
|
|
||||||
|
export const useMailDriverStore = (useWindow = false) => {
|
||||||
|
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
||||||
|
const { global } = window.i18n
|
||||||
|
|
||||||
|
return defineStoreFunc({
|
||||||
|
id: 'mail-driver',
|
||||||
|
|
||||||
|
state: () => ({
|
||||||
|
mailConfigData: null,
|
||||||
|
mail_driver: 'smtp',
|
||||||
|
mail_drivers: [],
|
||||||
|
|
||||||
|
basicMailConfig: {
|
||||||
|
mail_driver: '',
|
||||||
|
mail_host: '',
|
||||||
|
from_mail: '',
|
||||||
|
from_name: '',
|
||||||
|
},
|
||||||
|
|
||||||
|
mailgunConfig: {
|
||||||
|
mail_driver: '',
|
||||||
|
mail_mailgun_domain: '',
|
||||||
|
mail_mailgun_secret: '',
|
||||||
|
mail_mailgun_endpoint: '',
|
||||||
|
from_mail: '',
|
||||||
|
from_name: '',
|
||||||
|
},
|
||||||
|
|
||||||
|
sesConfig: {
|
||||||
|
mail_driver: '',
|
||||||
|
mail_host: '',
|
||||||
|
mail_port: null,
|
||||||
|
mail_ses_key: '',
|
||||||
|
mail_ses_secret: '',
|
||||||
|
mail_encryption: 'tls',
|
||||||
|
from_mail: '',
|
||||||
|
from_name: '',
|
||||||
|
},
|
||||||
|
|
||||||
|
smtpConfig: {
|
||||||
|
mail_driver: '',
|
||||||
|
mail_host: '',
|
||||||
|
mail_port: null,
|
||||||
|
mail_username: '',
|
||||||
|
mail_password: '',
|
||||||
|
mail_encryption: 'tls',
|
||||||
|
from_mail: '',
|
||||||
|
from_name: '',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
fetchMailDrivers() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axios
|
||||||
|
.get('/api/v1/mail/drivers')
|
||||||
|
.then((response) => {
|
||||||
|
if (response.data) {
|
||||||
|
this.mail_drivers = response.data
|
||||||
|
}
|
||||||
|
resolve(response)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
handleError(err)
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
fetchMailConfig() {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axios
|
||||||
|
.get('/api/v1/mail/config')
|
||||||
|
.then((response) => {
|
||||||
|
if (response.data) {
|
||||||
|
this.mailConfigData = response.data
|
||||||
|
this.mail_driver = response.data.mail_driver
|
||||||
|
}
|
||||||
|
resolve(response)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
handleError(err)
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
updateMailConfig(data) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axios
|
||||||
|
.post('/api/v1/mail/config', data)
|
||||||
|
.then((response) => {
|
||||||
|
const notificationStore = useNotificationStore()
|
||||||
|
if (response.data.success) {
|
||||||
|
notificationStore.showNotification({
|
||||||
|
type: 'success',
|
||||||
|
message: global.t('wizard.success.' + response.data.success),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
notificationStore.showNotification({
|
||||||
|
type: 'error',
|
||||||
|
message: global.t('wizard.errors.' + response.data.error),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
resolve(response)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
handleError(err)
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
sendTestMail(data) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axios
|
||||||
|
.post('/api/v1/mail/test', data)
|
||||||
|
.then((response) => {
|
||||||
|
const notificationStore = useNotificationStore()
|
||||||
|
if (response.data.success) {
|
||||||
|
notificationStore.showNotification({
|
||||||
|
type: 'success',
|
||||||
|
message: global.t('general.send_mail_successfully'),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
notificationStore.showNotification({
|
||||||
|
type: 'error',
|
||||||
|
message: global.t('validation.something_went_wrong'),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
resolve(response)
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
handleError(err)
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})()
|
||||||
|
}
|
||||||
@ -1,202 +0,0 @@
|
|||||||
import axios from 'axios'
|
|
||||||
import { defineStore } from 'pinia'
|
|
||||||
import { useNotificationStore } from '@/scripts/stores/notification'
|
|
||||||
import { handleError } from '@/scripts/helpers/error-handling'
|
|
||||||
import mailSenderStub from '@/scripts/admin/stub/mail-sender.js'
|
|
||||||
|
|
||||||
export const useMailSenderStore = (useWindow = false) => {
|
|
||||||
const pre_t = 'settings.mail_sender'
|
|
||||||
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
|
|
||||||
const { global } = window.i18n
|
|
||||||
|
|
||||||
return defineStoreFunc({
|
|
||||||
id: 'mailSender',
|
|
||||||
|
|
||||||
state: () => ({
|
|
||||||
mailSenders: [],
|
|
||||||
mail_drivers: [], // list of mail drivers
|
|
||||||
currentMailSender: { ...mailSenderStub.basicConfig },
|
|
||||||
smtpConfig: { ...mailSenderStub.smtpConfig },
|
|
||||||
mailgunConfig: { ...mailSenderStub.mailgunConfig },
|
|
||||||
sesConfig: { ...mailSenderStub.sesConfig },
|
|
||||||
mail_encryptions: ['none', 'tls', 'ssl', 'starttls'],
|
|
||||||
isDisable: false
|
|
||||||
}),
|
|
||||||
|
|
||||||
getters: {
|
|
||||||
isEdit: (state) => (state.currentMailSender.id ? true : false),
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
resetCurrentMailSender() {
|
|
||||||
this.currentMailSender = { ...mailSenderStub.basicConfig }
|
|
||||||
this.smtpConfig = { ...mailSenderStub.smtpConfig }
|
|
||||||
this.mailgunConfig = { ...mailSenderStub.mailgunConfig }
|
|
||||||
this.sesConfig = { ...mailSenderStub.sesConfig }
|
|
||||||
this.isDisable = false
|
|
||||||
},
|
|
||||||
|
|
||||||
fetchMailDrivers() {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
axios
|
|
||||||
.get('/api/v1/mail-drivers')
|
|
||||||
.then((response) => {
|
|
||||||
if (response.data) {
|
|
||||||
this.mail_drivers = response.data
|
|
||||||
}
|
|
||||||
resolve(response)
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
handleError(err)
|
|
||||||
reject(err)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
fetchMailSenders(params) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
axios
|
|
||||||
.get(`/api/v1/mail-senders`, { params })
|
|
||||||
.then((response) => {
|
|
||||||
this.mailSenders = response.data.data
|
|
||||||
resolve(response)
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
handleError(err)
|
|
||||||
reject(err)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
fetchMailSender(id) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
axios
|
|
||||||
.get(`/api/v1/mail-senders/${id}`)
|
|
||||||
.then((response) => {
|
|
||||||
this.currentMailSender = response.data.data
|
|
||||||
this.isDisable = response.data.data.is_default
|
|
||||||
if (response.data.data.settings) {
|
|
||||||
var settings = response.data.data.settings
|
|
||||||
const encryptionNone = settings.encryption == '' || settings.encryption == undefined
|
|
||||||
switch (response.data.data.driver) {
|
|
||||||
case 'smtp':
|
|
||||||
this.smtpConfig = settings
|
|
||||||
encryptionNone ? this.smtpConfig.encryption = 'none' : ''
|
|
||||||
break
|
|
||||||
case 'mailgun':
|
|
||||||
this.mailgunConfig = settings
|
|
||||||
break
|
|
||||||
case 'ses':
|
|
||||||
this.sesConfig = settings
|
|
||||||
encryptionNone ? this.sesConfig.encryption = 'none' : ''
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolve(response)
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
handleError(err)
|
|
||||||
reject(err)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
addMailSender(data) {
|
|
||||||
const notificationStore = useNotificationStore()
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
axios
|
|
||||||
.post('/api/v1/mail-senders', data)
|
|
||||||
.then((response) => {
|
|
||||||
this.mailSenders.push(response.data.data)
|
|
||||||
notificationStore.showNotification({
|
|
||||||
type: 'success',
|
|
||||||
message: global.t(`${pre_t}.created_message`),
|
|
||||||
})
|
|
||||||
resolve(response)
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
handleError(err)
|
|
||||||
reject(err)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
updateMailSender(data) {
|
|
||||||
const notificationStore = useNotificationStore()
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
axios
|
|
||||||
.put(`/api/v1/mail-senders/${data.id}`, data)
|
|
||||||
.then((response) => {
|
|
||||||
if (response.data) {
|
|
||||||
let pos = this.mailSenders.findIndex(
|
|
||||||
(mailSender) => mailSender.id === response.data.data.id
|
|
||||||
)
|
|
||||||
this.mailSenders[pos] = data
|
|
||||||
notificationStore.showNotification({
|
|
||||||
type: 'success',
|
|
||||||
message: global.t(`${pre_t}.updated_message`),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
resolve(response)
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
handleError(err)
|
|
||||||
reject(err)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
deleteMailSender(id) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
axios
|
|
||||||
.delete(`/api/v1/mail-senders/${id}`)
|
|
||||||
.then((response) => {
|
|
||||||
if (response.data.success) {
|
|
||||||
let index = this.mailSenders.findIndex(
|
|
||||||
(mailSender) => mailSender.id === id
|
|
||||||
)
|
|
||||||
this.mailSenders.splice(index, 1)
|
|
||||||
const notificationStore = useNotificationStore()
|
|
||||||
notificationStore.showNotification({
|
|
||||||
type: 'success',
|
|
||||||
message: global.t(`${pre_t}.deleted_message`),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
resolve(response)
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
handleError(err)
|
|
||||||
reject(err)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
sendTestMail(data) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
axios
|
|
||||||
.post('/api/v1/mail-test', data)
|
|
||||||
.then((response) => {
|
|
||||||
const notificationStore = useNotificationStore()
|
|
||||||
if (response.data.success) {
|
|
||||||
notificationStore.showNotification({
|
|
||||||
type: 'success',
|
|
||||||
message: global.t('general.send_mail_successfully'),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
notificationStore.showNotification({
|
|
||||||
type: 'error',
|
|
||||||
message: global.t('validation.something_went_wrong'),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
resolve(response)
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
handleError(err)
|
|
||||||
reject(err)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
@ -25,7 +25,6 @@ export const useUsersStore = (useWindow = false) => {
|
|||||||
password: null,
|
password: null,
|
||||||
phone: null,
|
phone: null,
|
||||||
companies: [],
|
companies: [],
|
||||||
sender_id: null,
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@ -64,13 +64,6 @@ export default {
|
|||||||
EDIT_ROLE: 'edit-role',
|
EDIT_ROLE: 'edit-role',
|
||||||
VIEW_ROLE: 'view-role',
|
VIEW_ROLE: 'view-role',
|
||||||
|
|
||||||
// Mail Sender
|
|
||||||
CREATE_MAIL_SENDER: 'view-mail-sender',
|
|
||||||
DELETE_MAIL_SENDER: 'delete-mail-sender',
|
|
||||||
EDIT_MAIL_SENDER: 'edit-mail-sender',
|
|
||||||
VIEW_MAIL_SENDER: 'view-mail-sender',
|
|
||||||
|
|
||||||
|
|
||||||
// exchange rates
|
// exchange rates
|
||||||
VIEW_EXCHANGE_RATE: 'view-exchange-rate-provider',
|
VIEW_EXCHANGE_RATE: 'view-exchange-rate-provider',
|
||||||
CREATE_EXCHANGE_RATE: 'create-exchange-rate-provider',
|
CREATE_EXCHANGE_RATE: 'create-exchange-rate-provider',
|
||||||
|
|||||||
@ -15,6 +15,5 @@ export default function () {
|
|||||||
customFields: [],
|
customFields: [],
|
||||||
fields: [],
|
fields: [],
|
||||||
enable_portal: false,
|
enable_portal: false,
|
||||||
mail_sender_id: null,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,31 +0,0 @@
|
|||||||
export default {
|
|
||||||
basicConfig: {
|
|
||||||
name: '',
|
|
||||||
from_name: '',
|
|
||||||
from_address: '',
|
|
||||||
cc: '',
|
|
||||||
bcc: '',
|
|
||||||
is_default: false,
|
|
||||||
driver: 'smtp', // 'smtp', 'mail', 'sendmail', 'mailgun', 'ses'
|
|
||||||
settings: '',
|
|
||||||
},
|
|
||||||
smtpConfig: {
|
|
||||||
host: '',
|
|
||||||
port: null,
|
|
||||||
username: '',
|
|
||||||
password: '',
|
|
||||||
encryption: 'tls', // 'tls', 'ssl', 'starttls'
|
|
||||||
},
|
|
||||||
mailgunConfig: {
|
|
||||||
domain: '',
|
|
||||||
secret: '',
|
|
||||||
endpoint: '',
|
|
||||||
},
|
|
||||||
sesConfig: {
|
|
||||||
host: '',
|
|
||||||
port: null,
|
|
||||||
encryption: 'tls', // 'tls', 'ssl', 'starttls'
|
|
||||||
ses_key: '',
|
|
||||||
ses_secret: '',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@ -256,7 +256,6 @@
|
|||||||
/> </template
|
/> </template
|
||||||
></BaseInput>
|
></BaseInput>
|
||||||
</BaseInputGroup>
|
</BaseInputGroup>
|
||||||
<!-- && setPasswordMethod !== 'manual' -->
|
|
||||||
</BaseInputGrid>
|
</BaseInputGrid>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -651,7 +650,10 @@ const rules = computed(() => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
email: {
|
email: {
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
required: helpers.withMessage(
|
||||||
|
t('validation.required'),
|
||||||
|
requiredIf(customerStore.currentCustomer.enable_portal == true)
|
||||||
|
),
|
||||||
email: helpers.withMessage(t('validation.email_incorrect'), email),
|
email: helpers.withMessage(t('validation.email_incorrect'), email),
|
||||||
},
|
},
|
||||||
password: {
|
password: {
|
||||||
|
|||||||
@ -3,238 +3,75 @@
|
|||||||
:title="$t('wizard.mail.mail_config')"
|
:title="$t('wizard.mail.mail_config')"
|
||||||
:description="$t('wizard.mail.mail_config_desc')"
|
:description="$t('wizard.mail.mail_config_desc')"
|
||||||
>
|
>
|
||||||
<form action="" @submit.prevent="submitMailSenderData">
|
<form action="" @submit.prevent="next">
|
||||||
<div class="p-4 sm:p-6 my-2">
|
<component
|
||||||
<!-- Name -->
|
:is="mailDriverStore.mail_driver"
|
||||||
<BaseInputGrid>
|
:config-data="mailDriverStore.mailConfigData"
|
||||||
<BaseInputGroup
|
:is-saving="isSaving"
|
||||||
:label="$t(`${pre_t}.name`)"
|
:is-fetching-initial-data="isFetchingInitialData"
|
||||||
:error="v$.name.$error && v$.name.$errors[0].$message"
|
@on-change-driver="(val) => changeDriver(val)"
|
||||||
:help-text="$t(`${pre_t}.name_help`)"
|
@submit-data="next"
|
||||||
required
|
/>
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model.trim="mailSenderStore.currentMailSender.name"
|
|
||||||
:invalid="v$.name.$error"
|
|
||||||
type="text"
|
|
||||||
@input="v$.name.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- From Name -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.from_name`)"
|
|
||||||
:error="v$.from_name.$error && v$.from_name.$errors[0].$message"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model="mailSenderStore.currentMailSender.from_name"
|
|
||||||
:invalid="v$.from_name.$error"
|
|
||||||
type="text"
|
|
||||||
@input="v$.from_name.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- From Address -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.from_address`)"
|
|
||||||
:error="
|
|
||||||
v$.from_address.$error && v$.from_address.$errors[0].$message
|
|
||||||
"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model="mailSenderStore.currentMailSender.from_address"
|
|
||||||
:invalid="v$.from_address.$error"
|
|
||||||
type="text"
|
|
||||||
@input="v$.from_address.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- CC -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.cc`)"
|
|
||||||
:error="v$.cc.$error && v$.cc.$errors[0].$message"
|
|
||||||
:help-text="$t(`${pre_t}.email_list`)"
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model="mailSenderStore.currentMailSender.cc"
|
|
||||||
:invalid="v$.cc.$error"
|
|
||||||
type="text"
|
|
||||||
@input="v$.cc.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- BCC -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.bcc`)"
|
|
||||||
:error="v$.bcc.$error && v$.bcc.$errors[0].$message"
|
|
||||||
:help-text="$t(`${pre_t}.email_list`)"
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model="mailSenderStore.currentMailSender.bcc"
|
|
||||||
:invalid="v$.bcc.$error"
|
|
||||||
type="text"
|
|
||||||
@input="v$.bcc.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- Mail Driver -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.driver`)"
|
|
||||||
:error="v$.driver.$error && v$.driver.$errors[0].$message"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<BaseMultiselect
|
|
||||||
v-model="mailSenderStore.currentMailSender.driver"
|
|
||||||
:options="mailSenderStore.mail_drivers"
|
|
||||||
:can-deselect="false"
|
|
||||||
:invalid="v$.driver.$error"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<component
|
|
||||||
:is="loadMailDriver"
|
|
||||||
:mail-sender-store="mailSenderStore"
|
|
||||||
/>
|
|
||||||
</BaseInputGrid>
|
|
||||||
<BaseDivider class="my-4" />
|
|
||||||
|
|
||||||
<BaseButton
|
|
||||||
:loading="isSaving"
|
|
||||||
:disabled="isSaving"
|
|
||||||
variant="primary"
|
|
||||||
type="submit"
|
|
||||||
>
|
|
||||||
<template #left="slotProps">
|
|
||||||
<BaseIcon
|
|
||||||
v-if="!isSaving"
|
|
||||||
name="SaveIcon"
|
|
||||||
:class="slotProps.class"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
{{ $t('wizard.save_cont') }}
|
|
||||||
</BaseButton>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
</BaseWizardStep>
|
</BaseWizardStep>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script>
|
||||||
import { computed, ref, inject, onMounted } from 'vue'
|
import Smtp from './mail-driver/SmtpMailDriver.vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import Mailgun from './mail-driver/MailgunMailDriver.vue'
|
||||||
import { useMailSenderStore } from '@/scripts/admin/stores/mail-sender'
|
import Ses from './mail-driver/SesMailDriver.vue'
|
||||||
import { useVuelidate } from '@vuelidate/core'
|
import Basic from './mail-driver/BasicMailDriver.vue'
|
||||||
import { required, email, minLength, helpers } from '@vuelidate/validators'
|
import { useMailDriverStore } from '@/scripts/admin/stores/mail-driver'
|
||||||
import MailSenderDropdown from '@/scripts/admin/components/dropdowns/MailSenderIndexDropdown.vue'
|
import { ref } from 'vue'
|
||||||
import MailSenderTestModal from '@/scripts/admin/components/modal-components/MailSenderTestModal.vue'
|
|
||||||
import SmtpDriver from '@/scripts/admin/views/settings/mail-sender/SmtpDriver.vue'
|
|
||||||
import MailgunDriver from '@/scripts/admin/views/settings/mail-sender/MailgunDriver.vue'
|
|
||||||
import SesDriver from '@/scripts/admin/views/settings/mail-sender/SesDriver.vue'
|
|
||||||
|
|
||||||
const pre_t = 'settings.mail_sender'
|
export default {
|
||||||
const mailSenderStore = useMailSenderStore()
|
components: {
|
||||||
const { t } = useI18n()
|
Smtp,
|
||||||
const table = ref(null)
|
Mailgun,
|
||||||
const utils = inject('utils')
|
Ses,
|
||||||
let isSaving = ref(false)
|
sendmail: Basic,
|
||||||
const emit = defineEmits(['next'])
|
Mail: Basic,
|
||||||
|
},
|
||||||
|
|
||||||
const loadMailDriver = computed(() => {
|
emits: ['next'],
|
||||||
switch (mailSenderStore.currentMailSender.driver) {
|
|
||||||
case 'smtp':
|
|
||||||
return SmtpDriver
|
|
||||||
break
|
|
||||||
case 'mail':
|
|
||||||
return false
|
|
||||||
break
|
|
||||||
case 'sendmail':
|
|
||||||
return false
|
|
||||||
break
|
|
||||||
case 'mailgun':
|
|
||||||
return MailgunDriver
|
|
||||||
break
|
|
||||||
case 'ses':
|
|
||||||
return SesDriver
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// This is multiple email custom validation
|
setup(props, { emit }) {
|
||||||
const multiEmail = (value) => {
|
const isSaving = ref(false)
|
||||||
if (value == '' || value === null) return true
|
const isFetchingInitialData = ref(false)
|
||||||
const emailRegex =
|
|
||||||
/^(?:[A-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[A-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9]{2,}(?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])$/i
|
|
||||||
|
|
||||||
const emailArr = value.split(',')
|
const mailDriverStore = useMailDriverStore()
|
||||||
let isValid = emailArr.every((v) => {
|
|
||||||
return emailRegex.test(v)
|
|
||||||
})
|
|
||||||
return isValid
|
|
||||||
}
|
|
||||||
|
|
||||||
const rules = computed(() => {
|
mailDriverStore.mail_driver = 'mail'
|
||||||
return {
|
|
||||||
name: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
minLength: helpers.withMessage(
|
|
||||||
t('validation.name_min_length', { count: 3 }),
|
|
||||||
minLength(3)
|
|
||||||
),
|
|
||||||
},
|
|
||||||
from_name: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
},
|
|
||||||
from_address: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
email: helpers.withMessage(t('validation.email_incorrect'), email),
|
|
||||||
},
|
|
||||||
cc: {
|
|
||||||
multiEmail: helpers.withMessage(
|
|
||||||
t('validation.email_incorrect'),
|
|
||||||
multiEmail
|
|
||||||
),
|
|
||||||
},
|
|
||||||
bcc: {
|
|
||||||
multiEmail: helpers.withMessage(
|
|
||||||
t('validation.email_incorrect'),
|
|
||||||
multiEmail
|
|
||||||
),
|
|
||||||
},
|
|
||||||
driver: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const v$ = useVuelidate(
|
loadData()
|
||||||
rules,
|
|
||||||
computed(() => mailSenderStore.currentMailSender)
|
|
||||||
)
|
|
||||||
|
|
||||||
async function submitMailSenderData() {
|
function changeDriver(value) {
|
||||||
v$.value.$touch()
|
mailDriverStore.mail_driver = value
|
||||||
if (v$.value.$invalid) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
let data = {
|
|
||||||
...mailSenderStore.currentMailSender,
|
|
||||||
is_default: true,
|
|
||||||
}
|
}
|
||||||
await mailSenderStore.addMailSender(data)
|
|
||||||
isSaving.value = true
|
|
||||||
emit('next')
|
|
||||||
isSaving.value = false
|
|
||||||
} catch (err) {
|
|
||||||
isSaving.value = false
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(async () => {
|
async function loadData() {
|
||||||
await mailSenderStore.fetchMailDrivers()
|
isFetchingInitialData.value = true
|
||||||
})
|
await mailDriverStore.fetchMailDrivers()
|
||||||
|
isFetchingInitialData.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
async function next(mailConfigData) {
|
||||||
|
isSaving.value = true
|
||||||
|
let res = await mailDriverStore.updateMailConfig(mailConfigData)
|
||||||
|
isSaving.value = false
|
||||||
|
|
||||||
|
if (res.data.success) {
|
||||||
|
await emit('next', 5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
mailDriverStore,
|
||||||
|
isSaving,
|
||||||
|
isFetchingInitialData,
|
||||||
|
changeDriver,
|
||||||
|
next,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -54,6 +54,8 @@
|
|||||||
label="name"
|
label="name"
|
||||||
:options="itemStore.itemUnits"
|
:options="itemStore.itemUnits"
|
||||||
value-prop="id"
|
value-prop="id"
|
||||||
|
:can-deselect="false"
|
||||||
|
:can-clear="false"
|
||||||
:placeholder="$t('items.select_a_unit')"
|
:placeholder="$t('items.select_a_unit')"
|
||||||
searchable
|
searchable
|
||||||
track-by="name"
|
track-by="name"
|
||||||
|
|||||||
@ -0,0 +1,158 @@
|
|||||||
|
<template>
|
||||||
|
<form @submit.prevent="saveEmailConfig">
|
||||||
|
<BaseInputGrid>
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.driver')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.basicMailConfig.mail_driver.$error &&
|
||||||
|
v$.basicMailConfig.mail_driver.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseMultiselect
|
||||||
|
v-model="mailDriverStore.basicMailConfig.mail_driver"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:options="mailDrivers"
|
||||||
|
:can-deselect="false"
|
||||||
|
:invalid="v$.basicMailConfig.mail_driver.$error"
|
||||||
|
@update:modelValue="onChangeDriver"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.from_mail')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.basicMailConfig.from_mail.$error &&
|
||||||
|
v$.basicMailConfig.from_mail.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseInput
|
||||||
|
v-model.trim="mailDriverStore.basicMailConfig.from_mail"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
type="text"
|
||||||
|
name="from_mail"
|
||||||
|
:invalid="v$.basicMailConfig.from_mail.$error"
|
||||||
|
@input="v$.basicMailConfig.from_mail.$touch()"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.from_name')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.basicMailConfig.from_name.$error &&
|
||||||
|
v$.basicMailConfig.from_name.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseInput
|
||||||
|
v-model.trim="mailDriverStore.basicMailConfig.from_name"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
type="text"
|
||||||
|
name="name"
|
||||||
|
:invalid="v$.basicMailConfig.from_name.$error"
|
||||||
|
@input="v$.basicMailConfig.from_name.$touch()"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
</BaseInputGrid>
|
||||||
|
<div class="flex mt-8">
|
||||||
|
<BaseButton
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:disabled="isSaving"
|
||||||
|
:loading="isSaving"
|
||||||
|
variant="primary"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
<template #left="slotProps">
|
||||||
|
<BaseIcon v-if="!isSaving" :class="slotProps.class" name="SaveIcon" />
|
||||||
|
</template>
|
||||||
|
{{ $t('general.save') }}
|
||||||
|
</BaseButton>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, computed } from 'vue'
|
||||||
|
import { required, email, helpers } from '@vuelidate/validators'
|
||||||
|
import useVuelidate from '@vuelidate/core'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { useMailDriverStore } from '@/scripts/admin/stores/mail-driver'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
configData: {
|
||||||
|
type: Object,
|
||||||
|
require: true,
|
||||||
|
default: Object,
|
||||||
|
},
|
||||||
|
isSaving: {
|
||||||
|
type: Boolean,
|
||||||
|
require: true,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
isFetchingInitialData: {
|
||||||
|
type: Boolean,
|
||||||
|
require: true,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
mailDrivers: {
|
||||||
|
type: Array,
|
||||||
|
require: true,
|
||||||
|
default: Array,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['submit-data', 'on-change-driver'])
|
||||||
|
|
||||||
|
const mailDriverStore = useMailDriverStore()
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const rules = computed(() => {
|
||||||
|
return {
|
||||||
|
basicMailConfig: {
|
||||||
|
mail_driver: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
},
|
||||||
|
from_mail: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
email: helpers.withMessage(t('validation.email_incorrect'), email),
|
||||||
|
},
|
||||||
|
from_name: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const v$ = useVuelidate(
|
||||||
|
rules,
|
||||||
|
computed(() => mailDriverStore)
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
for (const key in mailDriverStore.basicMailConfig) {
|
||||||
|
if (props.configData.hasOwnProperty(key)) {
|
||||||
|
mailDriverStore.$patch((state) => {
|
||||||
|
state.basicMailConfig[key] = props.configData[key]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
async function saveEmailConfig() {
|
||||||
|
v$.value.basicMailConfig.$touch()
|
||||||
|
if (!v$.value.basicMailConfig.$invalid) {
|
||||||
|
emit('submit-data', mailDriverStore.basicMailConfig)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChangeDriver() {
|
||||||
|
v$.value.basicMailConfig.mail_driver.$touch()
|
||||||
|
emit('on-change-driver', mailDriverStore.basicMailConfig.mail_driver)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -0,0 +1,247 @@
|
|||||||
|
<template>
|
||||||
|
<form @submit.prevent="saveEmailConfig">
|
||||||
|
<BaseInputGrid>
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.driver')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.mailgunConfig.mail_driver.$error &&
|
||||||
|
v$.mailgunConfig.mail_driver.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseMultiselect
|
||||||
|
v-model="mailDriverStore.mailgunConfig.mail_driver"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:options="mailDrivers"
|
||||||
|
:can-deselect="false"
|
||||||
|
:invalid="v$.mailgunConfig.mail_driver.$error"
|
||||||
|
@update:modelValue="onChangeDriver"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.mailgun_domain')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.mailgunConfig.mail_mailgun_domain.$error &&
|
||||||
|
v$.mailgunConfig.mail_mailgun_domain.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseInput
|
||||||
|
v-model.trim="mailDriverStore.mailgunConfig.mail_mailgun_domain"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
type="text"
|
||||||
|
name="mailgun_domain"
|
||||||
|
:invalid="v$.mailgunConfig.mail_mailgun_domain.$error"
|
||||||
|
@input="v$.mailgunConfig.mail_mailgun_domain.$touch()"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.mailgun_secret')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.mailgunConfig.mail_mailgun_secret.$error &&
|
||||||
|
v$.mailgunConfig.mail_mailgun_secret.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseInput
|
||||||
|
v-model.trim="mailDriverStore.mailgunConfig.mail_mailgun_secret"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:type="getInputType"
|
||||||
|
name="mailgun_secret"
|
||||||
|
autocomplete="off"
|
||||||
|
:invalid="v$.mailgunConfig.mail_mailgun_secret.$error"
|
||||||
|
@input="v$.mailgunConfig.mail_mailgun_secret.$touch()"
|
||||||
|
>
|
||||||
|
<template #right>
|
||||||
|
<BaseIcon
|
||||||
|
v-if="isShowPassword"
|
||||||
|
class="mr-1 text-gray-500 cursor-pointer"
|
||||||
|
name="EyeOffIcon"
|
||||||
|
@click="isShowPassword = !isShowPassword"
|
||||||
|
/>
|
||||||
|
<BaseIcon
|
||||||
|
v-else
|
||||||
|
class="mr-1 text-gray-500 cursor-pointer"
|
||||||
|
name="EyeIcon"
|
||||||
|
@click="isShowPassword = !isShowPassword"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</BaseInput>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.mailgun_endpoint')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.mailgunConfig.mail_mailgun_endpoint.$error &&
|
||||||
|
v$.mailgunConfig.mail_mailgun_endpoint.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseInput
|
||||||
|
v-model.trim="mailDriverStore.mailgunConfig.mail_mailgun_endpoint"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
type="text"
|
||||||
|
name="mailgun_endpoint"
|
||||||
|
:invalid="v$.mailgunConfig.mail_mailgun_endpoint.$error"
|
||||||
|
@input="v$.mailgunConfig.mail_mailgun_endpoint.$touch()"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.from_mail')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.mailgunConfig.from_mail.$error &&
|
||||||
|
v$.mailgunConfig.from_mail.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseInput
|
||||||
|
v-model.trim="mailDriverStore.mailgunConfig.from_mail"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
type="text"
|
||||||
|
name="from_mail"
|
||||||
|
:invalid="v$.mailgunConfig.from_mail.$error"
|
||||||
|
@input="v$.mailgunConfig.from_mail.$touch()"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.from_name')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.mailgunConfig.from_name.$error &&
|
||||||
|
v$.mailgunConfig.from_name.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseInput
|
||||||
|
v-model.trim="mailDriverStore.mailgunConfig.from_name"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
type="text"
|
||||||
|
name="from_name"
|
||||||
|
:invalid="v$.mailgunConfig.from_name.$error"
|
||||||
|
@input="v$.mailgunConfig.from_name.$touch()"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
</BaseInputGrid>
|
||||||
|
<div class="flex my-10">
|
||||||
|
<BaseButton
|
||||||
|
:disabled="isSaving"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:loading="isSaving"
|
||||||
|
variant="primary"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
<template #left="slotProps">
|
||||||
|
<BaseIcon v-if="!isSaving" name="SaveIcon" :class="slotProps.class" />
|
||||||
|
</template>
|
||||||
|
{{ $t('general.save') }}
|
||||||
|
</BaseButton>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref, computed } from 'vue'
|
||||||
|
import { required, email, helpers } from '@vuelidate/validators'
|
||||||
|
import useVuelidate from '@vuelidate/core'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { useMailDriverStore } from '@/scripts/admin/stores/mail-driver'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
configData: {
|
||||||
|
type: Object,
|
||||||
|
require: true,
|
||||||
|
default: Object,
|
||||||
|
},
|
||||||
|
isSaving: {
|
||||||
|
type: Boolean,
|
||||||
|
require: true,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
isFetchingInitialData: {
|
||||||
|
type: Boolean,
|
||||||
|
require: true,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
mailDrivers: {
|
||||||
|
type: Array,
|
||||||
|
require: true,
|
||||||
|
default: Array,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['submit-data', 'on-change-driver'])
|
||||||
|
|
||||||
|
const mailDriverStore = useMailDriverStore()
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
let isShowPassword = ref(false)
|
||||||
|
|
||||||
|
const getInputType = computed(() => {
|
||||||
|
if (isShowPassword.value) {
|
||||||
|
return 'text'
|
||||||
|
}
|
||||||
|
return 'password'
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules = computed(() => {
|
||||||
|
return {
|
||||||
|
mailgunConfig: {
|
||||||
|
mail_driver: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
},
|
||||||
|
mail_mailgun_domain: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
},
|
||||||
|
mail_mailgun_endpoint: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
},
|
||||||
|
mail_mailgun_secret: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
},
|
||||||
|
from_mail: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
email,
|
||||||
|
},
|
||||||
|
from_name: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const v$ = useVuelidate(
|
||||||
|
rules,
|
||||||
|
computed(() => mailDriverStore)
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
for (const key in mailDriverStore.mailgunConfig) {
|
||||||
|
if (props.configData.hasOwnProperty(key)) {
|
||||||
|
mailDriverStore.mailgunConfig[key] = props.configData[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
async function saveEmailConfig() {
|
||||||
|
v$.value.mailgunConfig.$touch()
|
||||||
|
if (!v$.value.mailgunConfig.$invalid) {
|
||||||
|
emit('submit-data', mailDriverStore.mailgunConfig)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChangeDriver() {
|
||||||
|
v$.value.mailgunConfig.mail_driver.$touch()
|
||||||
|
emit('on-change-driver', mailDriverStore.mailgunConfig.mail_driver)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -0,0 +1,294 @@
|
|||||||
|
<template>
|
||||||
|
<form @submit.prevent="saveEmailConfig">
|
||||||
|
<BaseInputGrid>
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.driver')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.sesConfig.mail_driver.$error &&
|
||||||
|
v$.sesConfig.mail_driver.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseMultiselect
|
||||||
|
v-model="mailDriverStore.sesConfig.mail_driver"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:options="mailDrivers"
|
||||||
|
:can-deselect="false"
|
||||||
|
:invalid="v$.sesConfig.mail_driver.$error"
|
||||||
|
@update:modelValue="onChangeDriver"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.host')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.sesConfig.mail_host.$error &&
|
||||||
|
v$.sesConfig.mail_host.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseInput
|
||||||
|
v-model.trim="mailDriverStore.sesConfig.mail_host"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
type="text"
|
||||||
|
name="mail_host"
|
||||||
|
:invalid="v$.sesConfig.mail_host.$error"
|
||||||
|
@input="v$.sesConfig.mail_host.$touch()"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.port')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.sesConfig.mail_port.$error &&
|
||||||
|
v$.sesConfig.mail_port.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseInput
|
||||||
|
v-model.trim="mailDriverStore.sesConfig.mail_port"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
type="text"
|
||||||
|
name="mail_port"
|
||||||
|
:invalid="v$.sesConfig.mail_port.$error"
|
||||||
|
@input="v$.sesConfig.mail_port.$touch()"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.encryption')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.sesConfig.mail_encryption.$error &&
|
||||||
|
v$.sesConfig.mail_encryption.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseMultiselect
|
||||||
|
v-model.trim="mailDriverStore.sesConfig.mail_encryption"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:options="encryptions"
|
||||||
|
:invalid="v$.sesConfig.mail_encryption.$error"
|
||||||
|
placeholder="Select option"
|
||||||
|
@input="v$.sesConfig.mail_encryption.$touch()"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.from_mail')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.sesConfig.from_mail.$error &&
|
||||||
|
v$.sesConfig.from_mail.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseInput
|
||||||
|
v-model.trim="mailDriverStore.sesConfig.from_mail"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
type="text"
|
||||||
|
name="from_mail"
|
||||||
|
:invalid="v$.sesConfig.from_mail.$error"
|
||||||
|
@input="v$.sesConfig.from_mail.$touch()"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.from_name')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.sesConfig.from_name.$error &&
|
||||||
|
v$.sesConfig.from_name.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseInput
|
||||||
|
v-model.trim="mailDriverStore.sesConfig.from_name"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
type="text"
|
||||||
|
name="name"
|
||||||
|
:invalid="v$.sesConfig.from_name.$error"
|
||||||
|
@input="v$.sesConfig.from_name.$touch()"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.ses_key')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.sesConfig.mail_ses_key.$error &&
|
||||||
|
v$.sesConfig.mail_ses_key.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseInput
|
||||||
|
v-model.trim="mailDriverStore.sesConfig.mail_ses_key"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
type="text"
|
||||||
|
name="mail_ses_key"
|
||||||
|
:invalid="v$.sesConfig.mail_ses_key.$error"
|
||||||
|
@input="v$.sesConfig.mail_ses_key.$touch()"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.ses_secret')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.sesConfig.mail_ses_secret.$error &&
|
||||||
|
v$.mail_ses_secret.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseInput
|
||||||
|
v-model.trim="mailDriverStore.sesConfig.mail_ses_secret"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:type="getInputType"
|
||||||
|
name="mail_ses_secret"
|
||||||
|
autocomplete="off"
|
||||||
|
:invalid="v$.sesConfig.mail_ses_secret.$error"
|
||||||
|
@input="v$.sesConfig.mail_ses_secret.$touch()"
|
||||||
|
>
|
||||||
|
<template #right>
|
||||||
|
<BaseIcon
|
||||||
|
v-if="isShowPassword"
|
||||||
|
class="mr-1 text-gray-500 cursor-pointer"
|
||||||
|
name="EyeOffIcon"
|
||||||
|
@click="isShowPassword = !isShowPassword"
|
||||||
|
/>
|
||||||
|
<BaseIcon
|
||||||
|
v-else
|
||||||
|
class="mr-1 text-gray-500 cursor-pointer"
|
||||||
|
name="EyeIcon"
|
||||||
|
@click="isShowPassword = !isShowPassword"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</BaseInput>
|
||||||
|
</BaseInputGroup>
|
||||||
|
</BaseInputGrid>
|
||||||
|
|
||||||
|
<div class="flex my-10">
|
||||||
|
<BaseButton
|
||||||
|
:disabled="isSaving"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:loading="isSaving"
|
||||||
|
variant="primary"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
<template #left="slotProps">
|
||||||
|
<BaseIcon v-if="!isSaving" name="SaveIcon" :class="slotProps.class" />
|
||||||
|
</template>
|
||||||
|
{{ $t('general.save') }}
|
||||||
|
</BaseButton>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, onMounted, reactive, ref } from 'vue'
|
||||||
|
import { required, email, numeric, helpers } from '@vuelidate/validators'
|
||||||
|
import useVuelidate from '@vuelidate/core'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { useMailDriverStore } from '@/scripts/admin/stores/mail-driver'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
configData: {
|
||||||
|
type: Object,
|
||||||
|
require: true,
|
||||||
|
default: Object,
|
||||||
|
},
|
||||||
|
isSaving: {
|
||||||
|
type: Boolean,
|
||||||
|
require: true,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
isFetchingInitialData: {
|
||||||
|
type: Boolean,
|
||||||
|
require: true,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
mailDrivers: {
|
||||||
|
type: Array,
|
||||||
|
require: true,
|
||||||
|
default: Array,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['submit-data', 'on-change-driver'])
|
||||||
|
|
||||||
|
const mailDriverStore = useMailDriverStore()
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
let isShowPassword = ref(false)
|
||||||
|
const encryptions = reactive(['tls', 'ssl', 'starttls'])
|
||||||
|
|
||||||
|
const rules = computed(() => {
|
||||||
|
return {
|
||||||
|
sesConfig: {
|
||||||
|
mail_driver: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
},
|
||||||
|
mail_host: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
},
|
||||||
|
mail_port: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
numeric,
|
||||||
|
},
|
||||||
|
mail_ses_key: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
},
|
||||||
|
mail_ses_secret: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
},
|
||||||
|
mail_encryption: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
},
|
||||||
|
from_mail: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
email: helpers.withMessage(t('validation.email_incorrect'), email),
|
||||||
|
},
|
||||||
|
from_name: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const v$ = useVuelidate(
|
||||||
|
rules,
|
||||||
|
computed(() => mailDriverStore)
|
||||||
|
)
|
||||||
|
|
||||||
|
const getInputType = computed(() => {
|
||||||
|
if (isShowPassword.value) {
|
||||||
|
return 'text'
|
||||||
|
}
|
||||||
|
return 'password'
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
for (const key in mailDriverStore.sesConfig) {
|
||||||
|
if (props.configData.hasOwnProperty(key)) {
|
||||||
|
mailDriverStore.sesConfig[key] = props.configData[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
async function saveEmailConfig() {
|
||||||
|
v$.value.sesConfig.$touch()
|
||||||
|
if (!v$.value.sesConfig.$invalid) {
|
||||||
|
emit('submit-data', mailDriverStore.sesConfig)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChangeDriver() {
|
||||||
|
v$.value.sesConfig.mail_driver.$touch()
|
||||||
|
emit('on-change-driver', mailDriverStore.sesConfig.mail_driver)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -0,0 +1,275 @@
|
|||||||
|
<template>
|
||||||
|
<form @submit.prevent="saveEmailConfig">
|
||||||
|
<BaseInputGrid>
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.driver')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.smtpConfig.mail_driver.$error &&
|
||||||
|
v$.smtpConfig.mail_driver.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseMultiselect
|
||||||
|
v-model="mailDriverStore.smtpConfig.mail_driver"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:options="mailDrivers"
|
||||||
|
:can-deselect="false"
|
||||||
|
:invalid="v$.smtpConfig.mail_driver.$error"
|
||||||
|
@update:modelValue="onChangeDriver"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.host')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.smtpConfig.mail_host.$error &&
|
||||||
|
v$.smtpConfig.mail_host.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseInput
|
||||||
|
v-model.trim="mailDriverStore.smtpConfig.mail_host"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
type="text"
|
||||||
|
name="mail_host"
|
||||||
|
:invalid="v$.smtpConfig.mail_host.$error"
|
||||||
|
@input="v$.smtpConfig.mail_host.$touch()"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:label="$t('settings.mail.username')"
|
||||||
|
>
|
||||||
|
<BaseInput
|
||||||
|
v-model.trim="mailDriverStore.smtpConfig.mail_username"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
type="text"
|
||||||
|
name="db_name"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:label="$t('settings.mail.password')"
|
||||||
|
>
|
||||||
|
<BaseInput
|
||||||
|
v-model.trim="mailDriverStore.smtpConfig.mail_password"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:type="getInputType"
|
||||||
|
name="password"
|
||||||
|
>
|
||||||
|
<template #right>
|
||||||
|
<BaseIcon
|
||||||
|
v-if="isShowPassword"
|
||||||
|
class="mr-1 text-gray-500 cursor-pointer"
|
||||||
|
name="EyeOffIcon"
|
||||||
|
@click="isShowPassword = !isShowPassword"
|
||||||
|
/>
|
||||||
|
<BaseIcon
|
||||||
|
v-else
|
||||||
|
class="mr-1 text-gray-500 cursor-pointer"
|
||||||
|
name="EyeIcon"
|
||||||
|
@click="isShowPassword = !isShowPassword"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</BaseInput>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.port')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.smtpConfig.mail_port.$error &&
|
||||||
|
v$.smtpConfig.mail_port.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseInput
|
||||||
|
v-model.trim="mailDriverStore.smtpConfig.mail_port"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
type="text"
|
||||||
|
name="mail_port"
|
||||||
|
:invalid="v$.smtpConfig.mail_port.$error"
|
||||||
|
@input="v$.smtpConfig.mail_port.$touch()"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.encryption')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.smtpConfig.mail_encryption.$error &&
|
||||||
|
v$.smtpConfig.mail_encryption.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseMultiselect
|
||||||
|
v-model.trim="mailDriverStore.smtpConfig.mail_encryption"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:options="encryptions"
|
||||||
|
:searchable="true"
|
||||||
|
:show-labels="false"
|
||||||
|
placeholder="Select option"
|
||||||
|
:invalid="v$.smtpConfig.mail_encryption.$error"
|
||||||
|
@input="v$.smtpConfig.mail_encryption.$touch()"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.from_mail')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.smtpConfig.from_mail.$error &&
|
||||||
|
v$.smtpConfig.from_mail.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseInput
|
||||||
|
v-model.trim="mailDriverStore.smtpConfig.from_mail"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
type="text"
|
||||||
|
name="from_mail"
|
||||||
|
:invalid="v$.smtpConfig.from_mail.$error"
|
||||||
|
@input="v$.smtpConfig.from_mail.$touch()"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
|
||||||
|
<BaseInputGroup
|
||||||
|
:label="$t('settings.mail.from_name')"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:error="
|
||||||
|
v$.smtpConfig.from_name.$error &&
|
||||||
|
v$.smtpConfig.from_name.$errors[0].$message
|
||||||
|
"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<BaseInput
|
||||||
|
v-model.trim="mailDriverStore.smtpConfig.from_name"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
type="text"
|
||||||
|
name="from_name"
|
||||||
|
:invalid="v$.smtpConfig.from_name.$error"
|
||||||
|
@input="v$.smtpConfig.from_name.$touch()"
|
||||||
|
/>
|
||||||
|
</BaseInputGroup>
|
||||||
|
</BaseInputGrid>
|
||||||
|
|
||||||
|
<div class="flex my-10">
|
||||||
|
<BaseButton
|
||||||
|
:disabled="isSaving"
|
||||||
|
:content-loading="isFetchingInitialData"
|
||||||
|
:loading="isSaving"
|
||||||
|
type="submit"
|
||||||
|
variant="primary"
|
||||||
|
>
|
||||||
|
<template #left="slotProps">
|
||||||
|
<BaseIcon v-if="!isSaving" name="SaveIcon" :class="slotProps.class" />
|
||||||
|
</template>
|
||||||
|
{{ $t('general.save') }}
|
||||||
|
</BaseButton>
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { reactive, onMounted, ref, computed } from 'vue'
|
||||||
|
import { required, email, numeric, helpers } from '@vuelidate/validators'
|
||||||
|
import useVuelidate from '@vuelidate/core'
|
||||||
|
import { useI18n } from 'vue-i18n'
|
||||||
|
import { useMailDriverStore } from '@/scripts/admin/stores/mail-driver'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
configData: {
|
||||||
|
type: Object,
|
||||||
|
require: true,
|
||||||
|
default: Object,
|
||||||
|
},
|
||||||
|
isSaving: {
|
||||||
|
type: Boolean,
|
||||||
|
require: true,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
isFetchingInitialData: {
|
||||||
|
type: Boolean,
|
||||||
|
require: true,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
mailDrivers: {
|
||||||
|
type: Array,
|
||||||
|
require: true,
|
||||||
|
default: Array,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['submit-data', 'on-change-driver'])
|
||||||
|
|
||||||
|
const mailDriverStore = useMailDriverStore()
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
let isShowPassword = ref(false)
|
||||||
|
const encryptions = reactive(['tls', 'ssl', 'starttls'])
|
||||||
|
|
||||||
|
const getInputType = computed(() => {
|
||||||
|
if (isShowPassword.value) {
|
||||||
|
return 'text'
|
||||||
|
}
|
||||||
|
return 'password'
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules = computed(() => {
|
||||||
|
return {
|
||||||
|
smtpConfig: {
|
||||||
|
mail_driver: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
},
|
||||||
|
mail_host: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
},
|
||||||
|
mail_port: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
numeric: helpers.withMessage(t('validation.numbers_only'), numeric),
|
||||||
|
},
|
||||||
|
mail_encryption: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
},
|
||||||
|
from_mail: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
email: helpers.withMessage(t('validation.email_incorrect'), email),
|
||||||
|
},
|
||||||
|
from_name: {
|
||||||
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const v$ = useVuelidate(
|
||||||
|
rules,
|
||||||
|
computed(() => mailDriverStore)
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
for (const key in mailDriverStore.smtpConfig) {
|
||||||
|
if (props.configData.hasOwnProperty(key)) {
|
||||||
|
mailDriverStore.smtpConfig[key] = props.configData[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
async function saveEmailConfig() {
|
||||||
|
v$.value.smtpConfig.$touch()
|
||||||
|
if (!v$.value.smtpConfig.$invalid) {
|
||||||
|
emit('submit-data', mailDriverStore.smtpConfig)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChangeDriver() {
|
||||||
|
v$.value.smtpConfig.mail_driver.$touch()
|
||||||
|
emit('on-change-driver', mailDriverStore.smtpConfig.mail_driver)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -1,136 +0,0 @@
|
|||||||
<template>
|
|
||||||
<BaseSettingCard
|
|
||||||
:title="$tc(`${pre_t}.title`, 2)"
|
|
||||||
:description="$t(`${pre_t}.description`)"
|
|
||||||
>
|
|
||||||
<MailSenderModal />
|
|
||||||
<MailSenderTestModal />
|
|
||||||
|
|
||||||
<template #action>
|
|
||||||
<BaseButton
|
|
||||||
type="submit"
|
|
||||||
variant="primary-outline"
|
|
||||||
@click="openMailSenderModal"
|
|
||||||
>
|
|
||||||
<template #left="slotProps">
|
|
||||||
<BaseIcon :class="slotProps.class" name="PlusIcon" />
|
|
||||||
</template>
|
|
||||||
{{ $t(`${pre_t}.add_new_mail_sender`) }}
|
|
||||||
</BaseButton>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<BaseTable
|
|
||||||
ref="table"
|
|
||||||
class="mt-16"
|
|
||||||
:data="fetchData"
|
|
||||||
:columns="mailSenderColumns"
|
|
||||||
>
|
|
||||||
<template #cell-is_default="{ row }">
|
|
||||||
<BaseBadge
|
|
||||||
:bg-color="
|
|
||||||
utils.getBadgeStatusColor(row.data.is_default ? 'YES' : 'NO')
|
|
||||||
.bgColor
|
|
||||||
"
|
|
||||||
:color="
|
|
||||||
utils.getBadgeStatusColor(row.data.is_default ? 'YES' : 'NO').color
|
|
||||||
"
|
|
||||||
>
|
|
||||||
{{ row.data.is_default ? $t('general.yes') : $t('general.no') }}
|
|
||||||
</BaseBadge>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #cell-actions="{ row }">
|
|
||||||
<MailSenderDropdown
|
|
||||||
:row="row.data"
|
|
||||||
:table="table"
|
|
||||||
:load-data="refreshTable"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</BaseTable>
|
|
||||||
</BaseSettingCard>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { computed, ref, inject } from 'vue'
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
import { useModalStore } from '@/scripts/stores/modal'
|
|
||||||
import MailSenderModal from '@/scripts/admin/components/modal-components/MailSenderModal.vue'
|
|
||||||
import { useMailSenderStore } from '@/scripts/admin/stores/mail-sender'
|
|
||||||
import MailSenderDropdown from '@/scripts/admin/components/dropdowns/MailSenderIndexDropdown.vue'
|
|
||||||
import MailSenderTestModal from '@/scripts/admin/components/modal-components/MailSenderTestModal.vue'
|
|
||||||
|
|
||||||
const pre_t = 'settings.mail_sender'
|
|
||||||
const modalStore = useModalStore()
|
|
||||||
const mailSenderStore = useMailSenderStore()
|
|
||||||
const { t } = useI18n()
|
|
||||||
const table = ref(null)
|
|
||||||
const utils = inject('utils')
|
|
||||||
|
|
||||||
function openMailSenderModal() {
|
|
||||||
modalStore.openModal({
|
|
||||||
title: t(`${pre_t}.add_new_mail_sender`),
|
|
||||||
componentName: 'MailSenderModal',
|
|
||||||
size: 'md',
|
|
||||||
refreshData: refreshTable,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const mailSenderColumns = computed(() => {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
key: 'name',
|
|
||||||
label: t(`${pre_t}.name`),
|
|
||||||
thClass: 'extra',
|
|
||||||
tdClass: 'font-medium text-gray-900',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'driver',
|
|
||||||
label: t(`${pre_t}.driver`),
|
|
||||||
thClass: 'extra',
|
|
||||||
tdClass: 'font-medium text-gray-900',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'from_address',
|
|
||||||
label: t(`${pre_t}.from_address`),
|
|
||||||
thClass: 'extra',
|
|
||||||
tdClass: 'font-medium text-gray-900',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'is_default',
|
|
||||||
label: t(`${pre_t}.is_default`),
|
|
||||||
thClass: 'extra',
|
|
||||||
tdClass: 'font-medium text-gray-900',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'actions',
|
|
||||||
label: '',
|
|
||||||
tdClass: 'text-right text-sm font-medium',
|
|
||||||
sortable: false,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
async function fetchData({ page, filter, sort }) {
|
|
||||||
let data = {
|
|
||||||
orderByField: sort.fieldName || 'created_at',
|
|
||||||
orderBy: sort.order || 'desc',
|
|
||||||
page,
|
|
||||||
}
|
|
||||||
|
|
||||||
let response = await mailSenderStore.fetchMailSenders(data)
|
|
||||||
|
|
||||||
return {
|
|
||||||
data: response.data.data,
|
|
||||||
pagination: {
|
|
||||||
totalPages: response.data.meta.last_page,
|
|
||||||
currentPage: page,
|
|
||||||
totalCount: response.data.meta.total,
|
|
||||||
limit: response.data.meta.per_page ? response.data.meta.per_page : 10,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function refreshTable() {
|
|
||||||
table.value && table.value.refresh()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@ -1,104 +0,0 @@
|
|||||||
<template>
|
|
||||||
<!-- Domain -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.domain`)"
|
|
||||||
:error="v$.domain.$error && v$.domain.$errors[0].$message"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model.trim="mailSenderStore.mailgunConfig.domain"
|
|
||||||
:invalid="v$.domain.$error"
|
|
||||||
type="text"
|
|
||||||
@input="v$.domain.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- Mailgun Secret -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.secret`)"
|
|
||||||
:error="v$.secret.$error && v$.secret.$errors[0].$message"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model="mailSenderStore.mailgunConfig.secret"
|
|
||||||
:type="getInputType"
|
|
||||||
autocomplete="off"
|
|
||||||
:invalid="v$.secret.$error"
|
|
||||||
@input="v$.secret.$touch()"
|
|
||||||
>
|
|
||||||
<template #right>
|
|
||||||
<BaseIcon
|
|
||||||
v-if="isShowPassword"
|
|
||||||
class="mr-1 text-gray-500 cursor-pointer"
|
|
||||||
name="EyeOffIcon"
|
|
||||||
@click="isShowPassword = !isShowPassword"
|
|
||||||
/>
|
|
||||||
<BaseIcon
|
|
||||||
v-else
|
|
||||||
class="mr-1 text-gray-500 cursor-pointer"
|
|
||||||
name="EyeIcon"
|
|
||||||
@click="isShowPassword = !isShowPassword"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</BaseInput>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- Mailgun Endpoint -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.endpoint`)"
|
|
||||||
:error="v$.endpoint.$error && v$.endpoint.$errors[0].$message"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model.trim="mailSenderStore.mailgunConfig.endpoint"
|
|
||||||
type="text"
|
|
||||||
:invalid="v$.endpoint.$error"
|
|
||||||
@input="v$.endpoint.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { computed, ref } from "vue"
|
|
||||||
import { useI18n } from "vue-i18n"
|
|
||||||
import { required, email, numeric, helpers } from "@vuelidate/validators"
|
|
||||||
import { useVuelidate } from "@vuelidate/core"
|
|
||||||
|
|
||||||
const pre_t = "settings.mail_sender.mailgun_config"
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
mailSenderStore: {
|
|
||||||
type: Object,
|
|
||||||
require: true,
|
|
||||||
default: Object,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
let isShowPassword = ref(false)
|
|
||||||
const getInputType = computed(() => {
|
|
||||||
if (isShowPassword.value) {
|
|
||||||
return "text"
|
|
||||||
}
|
|
||||||
return "password"
|
|
||||||
})
|
|
||||||
|
|
||||||
const rules = computed(() => {
|
|
||||||
return {
|
|
||||||
domain: {
|
|
||||||
required: helpers.withMessage(t("validation.required"), required),
|
|
||||||
},
|
|
||||||
endpoint: {
|
|
||||||
required: helpers.withMessage(t("validation.required"), required),
|
|
||||||
},
|
|
||||||
secret: {
|
|
||||||
required: helpers.withMessage(t("validation.required"), required),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const v$ = useVuelidate(
|
|
||||||
rules,
|
|
||||||
computed(() => props.mailSenderStore.mailgunConfig)
|
|
||||||
)
|
|
||||||
</script>
|
|
||||||
@ -1,143 +0,0 @@
|
|||||||
<template>
|
|
||||||
<!-- Host -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.host`)"
|
|
||||||
:error="v$.host.$error && v$.host.$errors[0].$message"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model.trim="mailSenderStore.sesConfig.host"
|
|
||||||
:invalid="v$.host.$error"
|
|
||||||
type="text"
|
|
||||||
@input="v$.host.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- Port -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.port`)"
|
|
||||||
:error="v$.port.$error && v$.port.$errors[0].$message"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model.trim="mailSenderStore.sesConfig.port"
|
|
||||||
type="text"
|
|
||||||
:invalid="v$.port.$error"
|
|
||||||
@input="v$.port.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- Encryption -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.encryption`)"
|
|
||||||
:error="v$.encryption.$error && v$.encryption.$errors[0].$message"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<BaseMultiselect
|
|
||||||
v-model.trim="mailSenderStore.sesConfig.encryption"
|
|
||||||
:options="encryptions"
|
|
||||||
:searchable="true"
|
|
||||||
:show-labels="false"
|
|
||||||
:placeholder="$t('general.select_option')"
|
|
||||||
:invalid="v$.encryption.$error"
|
|
||||||
@input="v$.encryption.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- SES Key -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.ses_key`)"
|
|
||||||
:error="v$.ses_key.$error && v$.ses_key.$errors[0].$message"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model.trim="mailSenderStore.sesConfig.ses_key"
|
|
||||||
type="text"
|
|
||||||
:invalid="v$.ses_key.$error"
|
|
||||||
@input="v$.ses_key.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- SES Secret -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.ses_secret`)"
|
|
||||||
:error="v$.ses_secret.$error && v$.ses_secret.$errors[0].$message"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model="mailSenderStore.sesConfig.ses_secret"
|
|
||||||
:type="getInputType"
|
|
||||||
autocomplete="off"
|
|
||||||
:invalid="v$.ses_secret.$error"
|
|
||||||
@input="v$.ses_secret.$touch()"
|
|
||||||
>
|
|
||||||
<template #right>
|
|
||||||
<BaseIcon
|
|
||||||
v-if="isShowPassword"
|
|
||||||
class="mr-1 text-gray-500 cursor-pointer"
|
|
||||||
name="EyeOffIcon"
|
|
||||||
@click="isShowPassword = !isShowPassword"
|
|
||||||
/>
|
|
||||||
<BaseIcon
|
|
||||||
v-else
|
|
||||||
class="mr-1 text-gray-500 cursor-pointer"
|
|
||||||
name="EyeIcon"
|
|
||||||
@click="isShowPassword = !isShowPassword"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</BaseInput>
|
|
||||||
</BaseInputGroup>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { computed, ref, reactive } from 'vue'
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
import { required, email, numeric, helpers } from '@vuelidate/validators'
|
|
||||||
import { useVuelidate } from '@vuelidate/core'
|
|
||||||
|
|
||||||
const pre_t = 'settings.mail_sender.ses_config'
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
mailSenderStore: {
|
|
||||||
type: Object,
|
|
||||||
require: true,
|
|
||||||
default: Object,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
let isShowPassword = ref(false)
|
|
||||||
const getInputType = computed(() => {
|
|
||||||
if (isShowPassword.value) {
|
|
||||||
return 'text'
|
|
||||||
}
|
|
||||||
return 'password'
|
|
||||||
})
|
|
||||||
const encryptions = props.mailSenderStore.mail_encryptions
|
|
||||||
|
|
||||||
const rules = computed(() => {
|
|
||||||
return {
|
|
||||||
host: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
},
|
|
||||||
port: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
numeric,
|
|
||||||
},
|
|
||||||
encryption: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
},
|
|
||||||
ses_key: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
},
|
|
||||||
ses_secret: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const v$ = useVuelidate(
|
|
||||||
rules,
|
|
||||||
computed(() => props.mailSenderStore.sesConfig)
|
|
||||||
)
|
|
||||||
</script>
|
|
||||||
@ -1,120 +0,0 @@
|
|||||||
<template>
|
|
||||||
<!-- Host -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.host`)"
|
|
||||||
:error="v$.host.$error && v$.host.$errors[0].$message"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model.trim="mailSenderStore.smtpConfig.host"
|
|
||||||
:invalid="v$.host.$error"
|
|
||||||
type="text"
|
|
||||||
@input="v$.host.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- Port -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.port`)"
|
|
||||||
:error="v$.port.$error && v$.port.$errors[0].$message"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<BaseInput
|
|
||||||
v-model.trim="mailSenderStore.smtpConfig.port"
|
|
||||||
type="text"
|
|
||||||
:invalid="v$.port.$error"
|
|
||||||
@input="v$.port.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- Username -->
|
|
||||||
<BaseInputGroup :label="$t(`${pre_t}.username`)">
|
|
||||||
<BaseInput v-model.trim="mailSenderStore.smtpConfig.username" type="text" />
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- Password -->
|
|
||||||
<BaseInputGroup :label="$t(`${pre_t}.password`)">
|
|
||||||
<BaseInput
|
|
||||||
v-model="mailSenderStore.smtpConfig.password"
|
|
||||||
:type="getInputType"
|
|
||||||
>
|
|
||||||
<template #right>
|
|
||||||
<BaseIcon
|
|
||||||
v-if="isShowPassword"
|
|
||||||
class="mr-1 text-gray-500 cursor-pointer"
|
|
||||||
name="EyeOffIcon"
|
|
||||||
@click="isShowPassword = !isShowPassword"
|
|
||||||
/>
|
|
||||||
<BaseIcon
|
|
||||||
v-else
|
|
||||||
class="mr-1 text-gray-500 cursor-pointer"
|
|
||||||
name="EyeIcon"
|
|
||||||
@click="isShowPassword = !isShowPassword"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</BaseInput>
|
|
||||||
</BaseInputGroup>
|
|
||||||
|
|
||||||
<!-- Encryption -->
|
|
||||||
<BaseInputGroup
|
|
||||||
:label="$t(`${pre_t}.encryption`)"
|
|
||||||
:error="v$.encryption.$error && v$.encryption.$errors[0].$message"
|
|
||||||
required
|
|
||||||
>
|
|
||||||
<BaseMultiselect
|
|
||||||
v-model.trim="mailSenderStore.smtpConfig.encryption"
|
|
||||||
:options="encryptions"
|
|
||||||
:searchable="true"
|
|
||||||
:show-labels="false"
|
|
||||||
:placeholder="$t('general.select_option')"
|
|
||||||
:invalid="v$.encryption.$error"
|
|
||||||
@input="v$.encryption.$touch()"
|
|
||||||
/>
|
|
||||||
</BaseInputGroup>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { computed, ref, reactive } from 'vue'
|
|
||||||
import { useI18n } from 'vue-i18n'
|
|
||||||
import { required, numeric, helpers } from '@vuelidate/validators'
|
|
||||||
import { useVuelidate } from '@vuelidate/core'
|
|
||||||
|
|
||||||
const pre_t = 'settings.mail_sender.smtp_config'
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
mailSenderStore: {
|
|
||||||
type: Object,
|
|
||||||
require: true,
|
|
||||||
default: Object,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
let isShowPassword = ref(false)
|
|
||||||
const getInputType = computed(() => {
|
|
||||||
if (isShowPassword.value) {
|
|
||||||
return 'text'
|
|
||||||
}
|
|
||||||
return 'password'
|
|
||||||
})
|
|
||||||
const encryptions = props.mailSenderStore.mail_encryptions
|
|
||||||
|
|
||||||
const rules = computed(() => {
|
|
||||||
return {
|
|
||||||
host: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
},
|
|
||||||
port: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
numeric: helpers.withMessage(t('validation.numbers_only'), numeric),
|
|
||||||
},
|
|
||||||
encryption: {
|
|
||||||
required: helpers.withMessage(t('validation.required'), required),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const v$ = useVuelidate(
|
|
||||||
rules,
|
|
||||||
computed(() => props.mailSenderStore.smtpConfig)
|
|
||||||
)
|
|
||||||
</script>
|
|
||||||
@ -162,7 +162,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, reactive, onMounted } from 'vue'
|
import { ref, computed, reactive } from 'vue'
|
||||||
import { useRouter, useRoute } from 'vue-router'
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
import { useCompanyStore } from '@/scripts/admin/stores/company'
|
import { useCompanyStore } from '@/scripts/admin/stores/company'
|
||||||
import {
|
import {
|
||||||
|
|||||||
@ -54,6 +54,7 @@
|
|||||||
bg-white
|
bg-white
|
||||||
rounded-lg
|
rounded-lg
|
||||||
text-left
|
text-left
|
||||||
|
overflow-hidden
|
||||||
relative
|
relative
|
||||||
shadow-xl
|
shadow-xl
|
||||||
transition-all
|
transition-all
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
"customers": "العملاء",
|
"customers": "العملاء",
|
||||||
"items": "بضائع/خدمات",
|
"items": "بضائع/خدمات",
|
||||||
"invoices": "الفواتير",
|
"invoices": "الفواتير",
|
||||||
"recurring-invoices": "Recurring Invoices",
|
"recurring-invoices": "الفواتير المتكررة",
|
||||||
"expenses": "النفقات",
|
"expenses": "النفقات",
|
||||||
"estimates": "التقديرات",
|
"estimates": "التقديرات",
|
||||||
"payments": "الدفوعات",
|
"payments": "الدفوعات",
|
||||||
@ -12,7 +12,7 @@
|
|||||||
"settings": "الإعدادات",
|
"settings": "الإعدادات",
|
||||||
"logout": "تسجيل الخروج",
|
"logout": "تسجيل الخروج",
|
||||||
"users": "المستخدمون",
|
"users": "المستخدمون",
|
||||||
"modules": "Modules"
|
"modules": "الإضافات"
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"add_company": "أضف شركة",
|
"add_company": "أضف شركة",
|
||||||
@ -29,9 +29,9 @@
|
|||||||
"to_date": "إلى تاريخ",
|
"to_date": "إلى تاريخ",
|
||||||
"from": "من",
|
"from": "من",
|
||||||
"to": "إلى",
|
"to": "إلى",
|
||||||
"ok": "Ok",
|
"ok": "حسناً",
|
||||||
"yes": "Yes",
|
"yes": "نعم",
|
||||||
"no": "No",
|
"no": "لا",
|
||||||
"sort_by": "ترتيب حسب",
|
"sort_by": "ترتيب حسب",
|
||||||
"ascending": "تصاعدي",
|
"ascending": "تصاعدي",
|
||||||
"descending": "تنازلي",
|
"descending": "تنازلي",
|
||||||
@ -39,7 +39,7 @@
|
|||||||
"body": "الجسم",
|
"body": "الجسم",
|
||||||
"message": "رسالة",
|
"message": "رسالة",
|
||||||
"send": "إرسال",
|
"send": "إرسال",
|
||||||
"preview": "Preview",
|
"preview": "معاينة",
|
||||||
"go_back": "إلى الخلف",
|
"go_back": "إلى الخلف",
|
||||||
"back_to_login": "العودة إلى تسجيل الدخول؟",
|
"back_to_login": "العودة إلى تسجيل الدخول؟",
|
||||||
"home": "الرئيسية",
|
"home": "الرئيسية",
|
||||||
@ -65,7 +65,7 @@
|
|||||||
"sent": "ارسلت",
|
"sent": "ارسلت",
|
||||||
"all": "الكل",
|
"all": "الكل",
|
||||||
"select_all": "تحديد الكل",
|
"select_all": "تحديد الكل",
|
||||||
"select_template": "Select Template",
|
"select_template": "تحديد القالب",
|
||||||
"choose_file": "اضغط هنا لاختيار ملف",
|
"choose_file": "اضغط هنا لاختيار ملف",
|
||||||
"choose_template": "اختيار القالب",
|
"choose_template": "اختيار القالب",
|
||||||
"choose": "اختر",
|
"choose": "اختر",
|
||||||
@ -93,14 +93,14 @@
|
|||||||
"no_note_found": "لم يتم العثور على الملاحظة",
|
"no_note_found": "لم يتم العثور على الملاحظة",
|
||||||
"insert_note": "أدخل ملاحظة",
|
"insert_note": "أدخل ملاحظة",
|
||||||
"copied_pdf_url_clipboard": "تم نسخ رابط PDF إلى الحافظة!",
|
"copied_pdf_url_clipboard": "تم نسخ رابط PDF إلى الحافظة!",
|
||||||
"copied_url_clipboard": "Copied url to clipboard!",
|
"copied_url_clipboard": "تم نسخ الرابط إلى الحافظة!",
|
||||||
"docs": "Docs",
|
"docs": "المستندات",
|
||||||
"do_you_wish_to_continue": "Do you wish to continue?",
|
"do_you_wish_to_continue": "هل ترغب في المتابعة؟",
|
||||||
"note": "Note",
|
"note": "ملاحظة",
|
||||||
"pay_invoice": "Pay Invoice",
|
"pay_invoice": "سدد الفاتورة",
|
||||||
"login_successfully": "Logged in successfully!",
|
"login_successfully": "تم تسجيل الدخول بنجاح!",
|
||||||
"logged_out_successfully": "Logged out successfully",
|
"logged_out_successfully": "تم تسجيل الخروج",
|
||||||
"mark_as_default": "Mark as default"
|
"mark_as_default": "تحديد كافتراضي"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"select_year": "اختر السنة",
|
"select_year": "اختر السنة",
|
||||||
@ -109,7 +109,7 @@
|
|||||||
"customers": "العملاء",
|
"customers": "العملاء",
|
||||||
"invoices": "الفواتير",
|
"invoices": "الفواتير",
|
||||||
"estimates": "التقديرات",
|
"estimates": "التقديرات",
|
||||||
"payments": "Payments"
|
"payments": "المدفوعات"
|
||||||
},
|
},
|
||||||
"chart_info": {
|
"chart_info": {
|
||||||
"total_sales": "المبيعات",
|
"total_sales": "المبيعات",
|
||||||
@ -151,27 +151,27 @@
|
|||||||
"no_results_found": "لم يتم العثور على نتائج"
|
"no_results_found": "لم يتم العثور على نتائج"
|
||||||
},
|
},
|
||||||
"company_switcher": {
|
"company_switcher": {
|
||||||
"label": "SWITCH COMPANY",
|
"label": "تبديل الشركة",
|
||||||
"no_results_found": "No Results Found",
|
"no_results_found": "لا توجد نتائج",
|
||||||
"add_new_company": "Add new company",
|
"add_new_company": "إضافة شركة جديدة",
|
||||||
"new_company": "New company",
|
"new_company": "شركة جديدة",
|
||||||
"created_message": "Company created successfully"
|
"created_message": "تم إنشاء الشركة بنجاح"
|
||||||
},
|
},
|
||||||
"dateRange": {
|
"dateRange": {
|
||||||
"today": "Today",
|
"today": "اليوم",
|
||||||
"this_week": "This Week",
|
"this_week": "هذا الاسبوع",
|
||||||
"this_month": "This Month",
|
"this_month": "هذا الشهر",
|
||||||
"this_quarter": "This Quarter",
|
"this_quarter": "هذا الربع",
|
||||||
"this_year": "This Year",
|
"this_year": "هذه السنة",
|
||||||
"previous_week": "Previous Week",
|
"previous_week": "الأسبوع السابق",
|
||||||
"previous_month": "Previous Month",
|
"previous_month": "الشهر الماضي",
|
||||||
"previous_quarter": "Previous Quarter",
|
"previous_quarter": "الربع السابق",
|
||||||
"previous_year": "Previous Year",
|
"previous_year": "السنة الماضية",
|
||||||
"custom": "Custom"
|
"custom": "مخصص"
|
||||||
},
|
},
|
||||||
"customers": {
|
"customers": {
|
||||||
"title": "العملاء",
|
"title": "العملاء",
|
||||||
"prefix": "Prefix",
|
"prefix": "البادئة",
|
||||||
"add_customer": "إضافة عميل",
|
"add_customer": "إضافة عميل",
|
||||||
"contacts_list": "قائمة العملاء",
|
"contacts_list": "قائمة العملاء",
|
||||||
"name": "الاسم",
|
"name": "الاسم",
|
||||||
@ -186,9 +186,9 @@
|
|||||||
"phone": "الهاتف",
|
"phone": "الهاتف",
|
||||||
"website": "موقع الإنترنت",
|
"website": "موقع الإنترنت",
|
||||||
"overview": "استعراض",
|
"overview": "استعراض",
|
||||||
"invoice_prefix": "Invoice Prefix",
|
"invoice_prefix": "بادئة الفاتورة",
|
||||||
"estimate_prefix": "Estimate Prefix",
|
"estimate_prefix": "بادئة رقم التقدير",
|
||||||
"payment_prefix": "Payment Prefix",
|
"payment_prefix": "بادئة رقم الدفعة",
|
||||||
"enable_portal": "تفعيل البوابة",
|
"enable_portal": "تفعيل البوابة",
|
||||||
"country": "الدولة",
|
"country": "الدولة",
|
||||||
"state": "الولاية/المنطقة",
|
"state": "الولاية/المنطقة",
|
||||||
@ -197,7 +197,7 @@
|
|||||||
"added_on": "أضيف في",
|
"added_on": "أضيف في",
|
||||||
"action": "إجراء",
|
"action": "إجراء",
|
||||||
"password": "كلمة المرور",
|
"password": "كلمة المرور",
|
||||||
"confirm_password": "Confirm Password",
|
"confirm_password": "تأكيد كلمة المرور",
|
||||||
"street_number": "رقم الشارع",
|
"street_number": "رقم الشارع",
|
||||||
"primary_currency": "العملة الرئيسية",
|
"primary_currency": "العملة الرئيسية",
|
||||||
"description": "الوصف",
|
"description": "الوصف",
|
||||||
@ -208,10 +208,10 @@
|
|||||||
"new_customer": "عميل جديد",
|
"new_customer": "عميل جديد",
|
||||||
"edit_customer": "تعديل عميل",
|
"edit_customer": "تعديل عميل",
|
||||||
"basic_info": "معلوات أساسية",
|
"basic_info": "معلوات أساسية",
|
||||||
"portal_access": "Portal Access",
|
"portal_access": "الوصول إلى بوابة العملاء",
|
||||||
"portal_access_text": "Would you like to allow this customer to login to the Customer Portal?",
|
"portal_access_text": "هل تريد السماح لهذا العميل بتسجيل الدخول إلى بوابة العملاء؟",
|
||||||
"portal_access_url": "Customer Portal Login URL",
|
"portal_access_url": "رابط بوابة العملاء",
|
||||||
"portal_access_url_help": "Please copy & forward the above given URL to your customer for providing access.",
|
"portal_access_url_help": "يرجى نسخ وإعادة إرسال عنوان URL أعلاه إلى الزبون لتوفير الوصول.",
|
||||||
"billing_address": "عنوان الفوترة",
|
"billing_address": "عنوان الفوترة",
|
||||||
"shipping_address": "عنوان الشحن",
|
"shipping_address": "عنوان الشحن",
|
||||||
"copy_billing_address": "نسخ من عنوان الفوترة",
|
"copy_billing_address": "نسخ من عنوان الفوترة",
|
||||||
@ -231,9 +231,9 @@
|
|||||||
"confirm_delete": "لن تتمكن من استرداد هذا العميل وجميع الفواتير والتقديرات والمدفوعات ذات الصلة. | لن تتمكن من استرداد هؤلاء العملاء وجميع الفواتير والتقديرات والمدفوعات ذات الصلة.",
|
"confirm_delete": "لن تتمكن من استرداد هذا العميل وجميع الفواتير والتقديرات والمدفوعات ذات الصلة. | لن تتمكن من استرداد هؤلاء العملاء وجميع الفواتير والتقديرات والمدفوعات ذات الصلة.",
|
||||||
"created_message": "تم إنشاء العملاء بنجاح",
|
"created_message": "تم إنشاء العملاء بنجاح",
|
||||||
"updated_message": "تم تحديث العملاء بنجاح",
|
"updated_message": "تم تحديث العملاء بنجاح",
|
||||||
"address_updated_message": "Address Information Updated succesfully",
|
"address_updated_message": "تم تحديث العنوان بنجاح",
|
||||||
"deleted_message": "تم حذف العملاء بنجاح | تم حذف العميل بنجاح",
|
"deleted_message": "تم حذف العملاء بنجاح | تم حذف العميل بنجاح",
|
||||||
"edit_currency_not_allowed": "Cannot change currency once transactions created."
|
"edit_currency_not_allowed": "لا يمكن تغيير العملة بمجرد إنشاء معاملة."
|
||||||
},
|
},
|
||||||
"items": {
|
"items": {
|
||||||
"title": "الأصناف",
|
"title": "الأصناف",
|
||||||
@ -265,8 +265,8 @@
|
|||||||
},
|
},
|
||||||
"estimates": {
|
"estimates": {
|
||||||
"title": "التقديرات",
|
"title": "التقديرات",
|
||||||
"accept_estimate": "Accept Estimate",
|
"accept_estimate": "قبول التقدير",
|
||||||
"reject_estimate": "Reject Estimate",
|
"reject_estimate": "رفض التقدير",
|
||||||
"estimate": "تقدير | تقديرات",
|
"estimate": "تقدير | تقديرات",
|
||||||
"estimates_list": "قائمة التقديرات",
|
"estimates_list": "قائمة التقديرات",
|
||||||
"days": "{days} أيام",
|
"days": "{days} أيام",
|
||||||
@ -318,10 +318,10 @@
|
|||||||
},
|
},
|
||||||
"accepted": "مقبول",
|
"accepted": "مقبول",
|
||||||
"rejected": "مرفوض",
|
"rejected": "مرفوض",
|
||||||
"expired": "Expired",
|
"expired": "انتهت مدة الصلاحية",
|
||||||
"sent": "مرسل",
|
"sent": "مرسل",
|
||||||
"draft": "مسودة",
|
"draft": "مسودة",
|
||||||
"viewed": "Viewed",
|
"viewed": "تمت المشاهدة",
|
||||||
"declined": "مرفوض",
|
"declined": "مرفوض",
|
||||||
"new_estimate": "تقدير جديد",
|
"new_estimate": "تقدير جديد",
|
||||||
"add_new_estimate": "إضافة تقدير جديد",
|
"add_new_estimate": "إضافة تقدير جديد",
|
||||||
@ -355,14 +355,14 @@
|
|||||||
"select_an_item": "اكتب أو اختر الصنف",
|
"select_an_item": "اكتب أو اختر الصنف",
|
||||||
"type_item_description": "اكتب وصف الصنف (اختياري)"
|
"type_item_description": "اكتب وصف الصنف (اختياري)"
|
||||||
},
|
},
|
||||||
"mark_as_default_estimate_template_description": "If enabled, the selected template will be automatically selected for new estimates."
|
"mark_as_default_estimate_template_description": "في حالة التعفيل، سيتم اختيار القالب المحدد تلقائياً للتقديرات الجديدة."
|
||||||
},
|
},
|
||||||
"invoices": {
|
"invoices": {
|
||||||
"title": "الفواتير",
|
"title": "الفواتير",
|
||||||
"download": "Download",
|
"download": "تحميل",
|
||||||
"pay_invoice": "Pay Invoice",
|
"pay_invoice": "سدد الفاتورة",
|
||||||
"invoices_list": "قائمة الفواتير",
|
"invoices_list": "قائمة الفواتير",
|
||||||
"invoice_information": "Invoice Information",
|
"invoice_information": "معلومات الفاتورة",
|
||||||
"days": "{days} أيام",
|
"days": "{days} أيام",
|
||||||
"months": "{months} أشهر",
|
"months": "{months} أشهر",
|
||||||
"years": "{years} سنوات",
|
"years": "{years} سنوات",
|
||||||
@ -397,13 +397,13 @@
|
|||||||
"send_invoice": "إرسال الفاتورة",
|
"send_invoice": "إرسال الفاتورة",
|
||||||
"resend_invoice": "إعادة إرسال الفاتورة",
|
"resend_invoice": "إعادة إرسال الفاتورة",
|
||||||
"invoice_template": "قالب الفاتورة",
|
"invoice_template": "قالب الفاتورة",
|
||||||
"conversion_message": "Invoice cloned successful",
|
"conversion_message": "تم استنساخ الفاتورة بنجاح",
|
||||||
"template": "قالب",
|
"template": "قالب",
|
||||||
"mark_as_sent": "تحديد كمرسل",
|
"mark_as_sent": "تحديد كمرسل",
|
||||||
"confirm_send_invoice": "سيتم إرسال هذه الفاتورة بالبريد الألكتروني إلى العميل",
|
"confirm_send_invoice": "سيتم إرسال هذه الفاتورة بالبريد الألكتروني إلى العميل",
|
||||||
"invoice_mark_as_sent": "سيتم تحديد هذه الفاتورة كمرسلة",
|
"invoice_mark_as_sent": "سيتم تحديد هذه الفاتورة كمرسلة",
|
||||||
"confirm_mark_as_accepted": "This invoice will be marked as Accepted",
|
"confirm_mark_as_accepted": "سيتم تحديد هذه الفاتورة كمقبولة",
|
||||||
"confirm_mark_as_rejected": "This invoice will be marked as Rejected",
|
"confirm_mark_as_rejected": "سيتم تحديد هذه الفاتورة كمرفوضة",
|
||||||
"confirm_send": "سيتم إرسال هذه الفاتورة بالبريد الألكتروني إلى العميل",
|
"confirm_send": "سيتم إرسال هذه الفاتورة بالبريد الألكتروني إلى العميل",
|
||||||
"invoice_date": "تاريخ الفاتورة",
|
"invoice_date": "تاريخ الفاتورة",
|
||||||
"record_payment": "تسجيل مدفوعات",
|
"record_payment": "تسجيل مدفوعات",
|
||||||
@ -415,13 +415,13 @@
|
|||||||
"update_invoice": "تحديث الفاتورة",
|
"update_invoice": "تحديث الفاتورة",
|
||||||
"add_new_tax": "إضافة ضريبة جديدة",
|
"add_new_tax": "إضافة ضريبة جديدة",
|
||||||
"no_invoices": "لا يوجد فواتير حتى الآن!",
|
"no_invoices": "لا يوجد فواتير حتى الآن!",
|
||||||
"mark_as_rejected": "Mark as rejected",
|
"mark_as_rejected": "تحديد كمرفوض",
|
||||||
"mark_as_accepted": "Mark as accepted",
|
"mark_as_accepted": "تحديد كمقبول",
|
||||||
"list_of_invoices": "قائمة الفواتير .",
|
"list_of_invoices": "قائمة الفواتير .",
|
||||||
"select_invoice": "اختر الفاتورة",
|
"select_invoice": "اختر الفاتورة",
|
||||||
"no_matching_invoices": "لا يوجد فواتير مطابقة!",
|
"no_matching_invoices": "لا يوجد فواتير مطابقة!",
|
||||||
"mark_as_sent_successfully": "تم تحديد الفاتورة كمرسلة بنجاح",
|
"mark_as_sent_successfully": "تم تحديد الفاتورة كمرسلة بنجاح",
|
||||||
"invoice_sent_successfully": "Invoice sent successfully",
|
"invoice_sent_successfully": "تم إرسال الفاتورة بنجاح",
|
||||||
"cloned_successfully": "تم استنساخ الفاتورة بنجاح",
|
"cloned_successfully": "تم استنساخ الفاتورة بنجاح",
|
||||||
"clone_invoice": "استنساخ الفاتورة",
|
"clone_invoice": "استنساخ الفاتورة",
|
||||||
"confirm_clone": "سيتم استنساخ هذه الفاتورة في فاتورة جديدة",
|
"confirm_clone": "سيتم استنساخ هذه الفاتورة في فاتورة جديدة",
|
||||||
@ -447,14 +447,14 @@
|
|||||||
"marked_as_sent_message": "تم إرسال الفاتورة بنجاح",
|
"marked_as_sent_message": "تم إرسال الفاتورة بنجاح",
|
||||||
"something_went_wrong": "خطأ غير معروف!",
|
"something_went_wrong": "خطأ غير معروف!",
|
||||||
"invalid_due_amount_message": "المبلغ النهائي للفاتورة لا يمكن أن يكون أقل من المبلغ المطلوب لها. رجاءاً حدث الفاتورة أو قم بحذف المدفوعات المرتبطة بها للاستمرار.",
|
"invalid_due_amount_message": "المبلغ النهائي للفاتورة لا يمكن أن يكون أقل من المبلغ المطلوب لها. رجاءاً حدث الفاتورة أو قم بحذف المدفوعات المرتبطة بها للاستمرار.",
|
||||||
"mark_as_default_invoice_template_description": "If enabled, the selected template will be automatically selected for new invoices."
|
"mark_as_default_invoice_template_description": "في حالة التفعيل، سيتم اختيار القالب المحدد تلقائياً للفواتير الجديدة."
|
||||||
},
|
},
|
||||||
"recurring_invoices": {
|
"recurring_invoices": {
|
||||||
"title": "Recurring Invoices",
|
"title": "الفواتير المتكررة",
|
||||||
"invoices_list": "Recurring Invoices List",
|
"invoices_list": "الفواتير المتكررة",
|
||||||
"days": "{days} Days",
|
"days": "{days} أيام",
|
||||||
"months": "{months} Month",
|
"months": "{months} أشهر",
|
||||||
"years": "{years} Year",
|
"years": "{years} سنوات",
|
||||||
"all": "All",
|
"all": "All",
|
||||||
"paid": "Paid",
|
"paid": "Paid",
|
||||||
"unpaid": "Unpaid",
|
"unpaid": "Unpaid",
|
||||||
@ -1485,7 +1485,7 @@
|
|||||||
"pdf_estimate_label": "تقدير",
|
"pdf_estimate_label": "تقدير",
|
||||||
"pdf_estimate_number": "رقم تقدير",
|
"pdf_estimate_number": "رقم تقدير",
|
||||||
"pdf_estimate_date": "تاريخ التقدير",
|
"pdf_estimate_date": "تاريخ التقدير",
|
||||||
"pdf_estimate_expire_date": "تاريخ انتهاء الصلاحية",
|
"pdf_estimate_expire_date": "Expiry Date",
|
||||||
"pdf_invoice_label": "الفاتورة",
|
"pdf_invoice_label": "الفاتورة",
|
||||||
"pdf_invoice_number": "رقم الفاتورة",
|
"pdf_invoice_number": "رقم الفاتورة",
|
||||||
"pdf_invoice_date": "تاريخ الفاتورة",
|
"pdf_invoice_date": "تاريخ الفاتورة",
|
||||||
|
|||||||
@ -112,7 +112,7 @@
|
|||||||
"payments": "Platby"
|
"payments": "Platby"
|
||||||
},
|
},
|
||||||
"chart_info": {
|
"chart_info": {
|
||||||
"total_sales": "Slevy",
|
"total_sales": "Prodeje",
|
||||||
"total_receipts": "Doklady",
|
"total_receipts": "Doklady",
|
||||||
"total_expense": "Výdaje",
|
"total_expense": "Výdaje",
|
||||||
"net_income": "Čistý příjem",
|
"net_income": "Čistý příjem",
|
||||||
@ -1485,7 +1485,7 @@
|
|||||||
"pdf_estimate_label": "Odhad",
|
"pdf_estimate_label": "Odhad",
|
||||||
"pdf_estimate_number": "Číslo odhadu",
|
"pdf_estimate_number": "Číslo odhadu",
|
||||||
"pdf_estimate_date": "Datum odhadu",
|
"pdf_estimate_date": "Datum odhadu",
|
||||||
"pdf_estimate_expire_date": "Doba platnosti",
|
"pdf_estimate_expire_date": "Datum expirace",
|
||||||
"pdf_invoice_label": "Faktura",
|
"pdf_invoice_label": "Faktura",
|
||||||
"pdf_invoice_number": "Číslo faktury",
|
"pdf_invoice_number": "Číslo faktury",
|
||||||
"pdf_invoice_date": "Datum fakturace",
|
"pdf_invoice_date": "Datum fakturace",
|
||||||
@ -1495,7 +1495,7 @@
|
|||||||
"pdf_quantity_label": "Množství",
|
"pdf_quantity_label": "Množství",
|
||||||
"pdf_price_label": "Cena",
|
"pdf_price_label": "Cena",
|
||||||
"pdf_discount_label": "Sleva",
|
"pdf_discount_label": "Sleva",
|
||||||
"pdf_amount_label": "Množství",
|
"pdf_amount_label": "Částka",
|
||||||
"pdf_subtotal": "Mezisoučet",
|
"pdf_subtotal": "Mezisoučet",
|
||||||
"pdf_total": "Celkem",
|
"pdf_total": "Celkem",
|
||||||
"pdf_payment_label": "Platba",
|
"pdf_payment_label": "Platba",
|
||||||
|
|||||||
@ -688,7 +688,7 @@
|
|||||||
"other_modules": "Weitere Module",
|
"other_modules": "Weitere Module",
|
||||||
"view_all": "Alle Anzeigen",
|
"view_all": "Alle Anzeigen",
|
||||||
"no_reviews_found": "Für dieses Modul gibt es noch keine Bewertungen!",
|
"no_reviews_found": "Für dieses Modul gibt es noch keine Bewertungen!",
|
||||||
"module_not_purchased": "Module Not Purchased",
|
"module_not_purchased": "Modul noch nicht erworben",
|
||||||
"module_not_found": "Modul nicht gefunden",
|
"module_not_found": "Modul nicht gefunden",
|
||||||
"version_not_supported": "This module version doesn't support the current version of Crater",
|
"version_not_supported": "This module version doesn't support the current version of Crater",
|
||||||
"last_updated": "Zuletzt aktualisiert am",
|
"last_updated": "Zuletzt aktualisiert am",
|
||||||
@ -1113,7 +1113,7 @@
|
|||||||
"default_currency_error": "Diese Währung wird bereits in einem der aktiven Anbieter verwendet",
|
"default_currency_error": "Diese Währung wird bereits in einem der aktiven Anbieter verwendet",
|
||||||
"exchange_help_text": "Wechselkurs eingeben um von {currency} nach {baseCurrency} zu konvertieren",
|
"exchange_help_text": "Wechselkurs eingeben um von {currency} nach {baseCurrency} zu konvertieren",
|
||||||
"currency_freak": "CurrencyFreaks",
|
"currency_freak": "CurrencyFreaks",
|
||||||
"currency_layer": "Currency Layer",
|
"currency_layer": "Währungsebene",
|
||||||
"open_exchange_rate": "Open Exchange Rate",
|
"open_exchange_rate": "Open Exchange Rate",
|
||||||
"currency_converter": "Währungsumrechner",
|
"currency_converter": "Währungsumrechner",
|
||||||
"server": "Server",
|
"server": "Server",
|
||||||
@ -1150,8 +1150,8 @@
|
|||||||
"payment_mode_added": "Zahlungsart hinzugefügt",
|
"payment_mode_added": "Zahlungsart hinzugefügt",
|
||||||
"payment_mode_updated": "Zahlungsart aktualisiert",
|
"payment_mode_updated": "Zahlungsart aktualisiert",
|
||||||
"payment_mode_confirm_delete": "Sie werden diese Zahlungsart nicht wiederherstellen können",
|
"payment_mode_confirm_delete": "Sie werden diese Zahlungsart nicht wiederherstellen können",
|
||||||
"payments_attached": "This payment method is already attached to payments. Please delete the attached payments to proceed with deletion.",
|
"payments_attached": "Diese Zahlungsmethode ist bereits mit Zahlungen verknüpft. Bitte löschen Sie die angehängten Zahlungen, um mit der Löschung fortzufahren.",
|
||||||
"expenses_attached": "This payment method is already attached to expenses. Please delete the attached expenses to proceed with deletion.",
|
"expenses_attached": "Diese Zahlungsmethode ist bereits mit Ausgaben verknüpft. Bitte löschen Sie die angehängten Ausgaben, um mit der Löschung fortzufahren.",
|
||||||
"deleted_message": "Zahlungsart erfolgreich gelöscht"
|
"deleted_message": "Zahlungsart erfolgreich gelöscht"
|
||||||
},
|
},
|
||||||
"expense_category": {
|
"expense_category": {
|
||||||
@ -1179,7 +1179,7 @@
|
|||||||
"discount_per_item": "Rabatt pro Artikel ",
|
"discount_per_item": "Rabatt pro Artikel ",
|
||||||
"discount_setting_description": "Aktivieren Sie diese Option, wenn Sie einzelnen Rechnungspositionen einen Rabatt hinzufügen möchten. Standardmäßig wird der Rabatt direkt zur Rechnung hinzugefügt.",
|
"discount_setting_description": "Aktivieren Sie diese Option, wenn Sie einzelnen Rechnungspositionen einen Rabatt hinzufügen möchten. Standardmäßig wird der Rabatt direkt zur Rechnung hinzugefügt.",
|
||||||
"expire_public_links": "Öffentliche Links automatisch ablaufen lassen",
|
"expire_public_links": "Öffentliche Links automatisch ablaufen lassen",
|
||||||
"expire_setting_description": "Specify whether you would like to expire all the links sent by application to view invoices, estimates & payments, etc after a specified duration.",
|
"expire_setting_description": "Geben Sie an, ob Sie alle von der Anwendung gesendeten Links zur Ansicht von Rechnungen, Kostenvoranschlägen und Zahlungen usw. nach einer bestimmten Zeit ablaufen lassen möchten.",
|
||||||
"save": "Speichern",
|
"save": "Speichern",
|
||||||
"preference": "Präferenz | Präferenzen",
|
"preference": "Präferenz | Präferenzen",
|
||||||
"general_settings": "Standardeinstellungen für das System.",
|
"general_settings": "Standardeinstellungen für das System.",
|
||||||
@ -1272,14 +1272,14 @@
|
|||||||
"do_spaces_secret": "Do Spaces Secret",
|
"do_spaces_secret": "Do Spaces Secret",
|
||||||
"do_spaces_region": "Do Spaced Region",
|
"do_spaces_region": "Do Spaced Region",
|
||||||
"do_spaces_bucket": "Do Spaces Bucket",
|
"do_spaces_bucket": "Do Spaces Bucket",
|
||||||
"do_spaces_endpoint": "Do Spaces Endpoint",
|
"do_spaces_endpoint": "Do Spaces Endpunkt",
|
||||||
"do_spaces_root": "Do Spaced Root",
|
"do_spaces_root": "Do Spaced Root",
|
||||||
"dropbox_type": "Dropbox Typ",
|
"dropbox_type": "Dropbox Typ",
|
||||||
"dropbox_token": "Dropbox Token",
|
"dropbox_token": "Dropbox Token",
|
||||||
"dropbox_key": "Dropbox Schlüssel",
|
"dropbox_key": "Dropbox Schlüssel",
|
||||||
"dropbox_secret": "Dropbox Secret",
|
"dropbox_secret": "Dropbox Secret",
|
||||||
"dropbox_app": "Dropbox App",
|
"dropbox_app": "Dropbox App",
|
||||||
"dropbox_root": "Dropbox Root",
|
"dropbox_root": "Dropbox Root Verzeichnis",
|
||||||
"default_driver": "Standard-Treiber",
|
"default_driver": "Standard-Treiber",
|
||||||
"is_default": "Standard",
|
"is_default": "Standard",
|
||||||
"set_default_disk": "Als Standard festlegen",
|
"set_default_disk": "Als Standard festlegen",
|
||||||
@ -1485,7 +1485,7 @@
|
|||||||
"pdf_estimate_label": "Angebot",
|
"pdf_estimate_label": "Angebot",
|
||||||
"pdf_estimate_number": "Angebotsnummer",
|
"pdf_estimate_number": "Angebotsnummer",
|
||||||
"pdf_estimate_date": "Angebotsdatum",
|
"pdf_estimate_date": "Angebotsdatum",
|
||||||
"pdf_estimate_expire_date": "Gültig bis",
|
"pdf_estimate_expire_date": "Zahlungsziel",
|
||||||
"pdf_invoice_label": "Rechnung",
|
"pdf_invoice_label": "Rechnung",
|
||||||
"pdf_invoice_number": "Rechnungsnummer",
|
"pdf_invoice_number": "Rechnungsnummer",
|
||||||
"pdf_invoice_date": "Rechnungsdatum",
|
"pdf_invoice_date": "Rechnungsdatum",
|
||||||
|
|||||||
@ -1485,7 +1485,7 @@
|
|||||||
"pdf_estimate_label": "Εκτίμηση",
|
"pdf_estimate_label": "Εκτίμηση",
|
||||||
"pdf_estimate_number": "Εκτίμηση Αριθμού",
|
"pdf_estimate_number": "Εκτίμηση Αριθμού",
|
||||||
"pdf_estimate_date": "Εκτιμώμενη ημ. επισκευής",
|
"pdf_estimate_date": "Εκτιμώμενη ημ. επισκευής",
|
||||||
"pdf_estimate_expire_date": "Ημερομηνία λήξης",
|
"pdf_estimate_expire_date": "Expiry Date",
|
||||||
"pdf_invoice_label": "Τιμολόγιο",
|
"pdf_invoice_label": "Τιμολόγιο",
|
||||||
"pdf_invoice_number": "Αριθμός τιμολογίου",
|
"pdf_invoice_number": "Αριθμός τιμολογίου",
|
||||||
"pdf_invoice_date": "Ημ/νία Τιμολόγησης",
|
"pdf_invoice_date": "Ημ/νία Τιμολόγησης",
|
||||||
|
|||||||
@ -100,9 +100,7 @@
|
|||||||
"pay_invoice": "Pay Invoice",
|
"pay_invoice": "Pay Invoice",
|
||||||
"login_successfully": "Logged in successfully!",
|
"login_successfully": "Logged in successfully!",
|
||||||
"logged_out_successfully": "Logged out successfully",
|
"logged_out_successfully": "Logged out successfully",
|
||||||
"mark_as_default": "Mark as default",
|
"mark_as_default": "Mark as default"
|
||||||
"select_option": "Select option",
|
|
||||||
"send_test_mail": "Send Test Mail"
|
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"select_year": "Select year",
|
"select_year": "Select year",
|
||||||
@ -235,8 +233,7 @@
|
|||||||
"updated_message": "Customer updated successfully",
|
"updated_message": "Customer updated successfully",
|
||||||
"address_updated_message": "Address Information Updated succesfully",
|
"address_updated_message": "Address Information Updated succesfully",
|
||||||
"deleted_message": "Customer deleted successfully | Customers deleted successfully",
|
"deleted_message": "Customer deleted successfully | Customers deleted successfully",
|
||||||
"edit_currency_not_allowed": "Cannot change currency once transactions created.",
|
"edit_currency_not_allowed": "Cannot change currency once transactions created."
|
||||||
"select_sender": "Select Sender"
|
|
||||||
},
|
},
|
||||||
"items": {
|
"items": {
|
||||||
"title": "Items",
|
"title": "Items",
|
||||||
@ -731,8 +728,7 @@
|
|||||||
"updated_message": "User updated successfully",
|
"updated_message": "User updated successfully",
|
||||||
"deleted_message": "User deleted successfully | Users deleted successfully",
|
"deleted_message": "User deleted successfully | Users deleted successfully",
|
||||||
"select_company_role": "Select Role for {company}",
|
"select_company_role": "Select Role for {company}",
|
||||||
"companies": "Companies",
|
"companies": "Companies"
|
||||||
"select_sender": "Select Sender"
|
|
||||||
},
|
},
|
||||||
"reports": {
|
"reports": {
|
||||||
"title": "Report",
|
"title": "Report",
|
||||||
@ -811,8 +807,7 @@
|
|||||||
"payment_modes": "Payment Modes",
|
"payment_modes": "Payment Modes",
|
||||||
"notes": "Notes",
|
"notes": "Notes",
|
||||||
"exchange_rate": "Exchange Rate",
|
"exchange_rate": "Exchange Rate",
|
||||||
"address_information": "Address Information",
|
"address_information": "Address Information"
|
||||||
"mail_sender": "Mail Senders"
|
|
||||||
},
|
},
|
||||||
"address_information": {
|
"address_information": {
|
||||||
"section_description": " You can update Your Address information using form below."
|
"section_description": " You can update Your Address information using form below."
|
||||||
@ -1316,51 +1311,6 @@
|
|||||||
"state_placeholder": "Example: CA",
|
"state_placeholder": "Example: CA",
|
||||||
"zip_placeholder": "Example: 90024",
|
"zip_placeholder": "Example: 90024",
|
||||||
"invalid_address": "Please provide valid address details."
|
"invalid_address": "Please provide valid address details."
|
||||||
},
|
|
||||||
"mail_sender": {
|
|
||||||
"title": "Mail Sender | Mail Senders",
|
|
||||||
"description": "Configure & test your mail senders for the selected company.",
|
|
||||||
"add_new_mail_sender": "New Mail Sender",
|
|
||||||
"name": "Sender Name",
|
|
||||||
"name_help": "Type a name to identify the sender for users.",
|
|
||||||
"driver": "Mail Driver",
|
|
||||||
"is_default": "Set as default",
|
|
||||||
"is_default_description": "You can only set one sender as default at a given time.",
|
|
||||||
"cc": "CC",
|
|
||||||
"bcc": "BCC",
|
|
||||||
"from_address": "From Mail Address",
|
|
||||||
"from_name": "From Mail Name",
|
|
||||||
"edit_mail_sender": "Edit Mail Sender",
|
|
||||||
"delete_mail_sender": "Delete Mail Sender",
|
|
||||||
"confirm_delete": "You will not be able to recover this Mail Sender",
|
|
||||||
"created_message": "Mail Sender created successfully",
|
|
||||||
"updated_message": "Mail Sender updated successfully",
|
|
||||||
"deleted_message": "Mail Sender deleted successfully",
|
|
||||||
"default_record_exists": "Default mail sender already exist",
|
|
||||||
"email_list": "Supports a comma separated list of email addresses",
|
|
||||||
"select_mail_sender": "Select Mail Sender",
|
|
||||||
"manage_mail_sender": "Manage Mail Senders",
|
|
||||||
"no_mail_sender_found": "No mail senders found!",
|
|
||||||
"no_mail_sender_found_description": "You must configure at-least one mail sender for the selected company in order to continue.",
|
|
||||||
"smtp_config": {
|
|
||||||
"host": "Host",
|
|
||||||
"port": "Port",
|
|
||||||
"username": "Username",
|
|
||||||
"password": "Password",
|
|
||||||
"encryption": "Encryption"
|
|
||||||
},
|
|
||||||
"mailgun_config": {
|
|
||||||
"domain": "Domain",
|
|
||||||
"secret": "Maingun Secret",
|
|
||||||
"endpoint": "Mailgun Endpoint"
|
|
||||||
},
|
|
||||||
"ses_config": {
|
|
||||||
"host": "Host",
|
|
||||||
"port": "Port",
|
|
||||||
"encryption": "Encryption",
|
|
||||||
"ses_key": "SES Key",
|
|
||||||
"ses_secret": "SES Secret"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"wizard": {
|
"wizard": {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"navigation": {
|
"navigation": {
|
||||||
"dashboard": "Tablero",
|
"dashboard": "Panel de Control",
|
||||||
"customers": "Clientes",
|
"customers": "Clientes",
|
||||||
"items": "Artículos",
|
"items": "Artículos",
|
||||||
"invoices": "Facturas",
|
"invoices": "Facturas",
|
||||||
@ -9,7 +9,7 @@
|
|||||||
"estimates": "Presupuestos",
|
"estimates": "Presupuestos",
|
||||||
"payments": "Pagos",
|
"payments": "Pagos",
|
||||||
"reports": "Informes",
|
"reports": "Informes",
|
||||||
"settings": "Ajustes",
|
"settings": "Configuración",
|
||||||
"logout": "Cerrar sesión",
|
"logout": "Cerrar sesión",
|
||||||
"users": "Usuarios",
|
"users": "Usuarios",
|
||||||
"modules": "Módulos"
|
"modules": "Módulos"
|
||||||
@ -47,7 +47,7 @@
|
|||||||
"delete": "Eliminar",
|
"delete": "Eliminar",
|
||||||
"edit": "Editar",
|
"edit": "Editar",
|
||||||
"view": "Ver",
|
"view": "Ver",
|
||||||
"add_new_item": "Agregar ítem nuevo",
|
"add_new_item": "Agregar Nuevo Artículo",
|
||||||
"clear_all": "Limpiar todo",
|
"clear_all": "Limpiar todo",
|
||||||
"showing": "Mostrar",
|
"showing": "Mostrar",
|
||||||
"of": "de",
|
"of": "de",
|
||||||
@ -87,7 +87,7 @@
|
|||||||
"select_city": "Seleccionar ciudad",
|
"select_city": "Seleccionar ciudad",
|
||||||
"street_1": "Calle 1",
|
"street_1": "Calle 1",
|
||||||
"street_2": "Calle 2",
|
"street_2": "Calle 2",
|
||||||
"action_failed": "Accion Fallida",
|
"action_failed": "Acción Fallida",
|
||||||
"retry": "Procesar de nuevo",
|
"retry": "Procesar de nuevo",
|
||||||
"choose_note": "Elegir nota",
|
"choose_note": "Elegir nota",
|
||||||
"no_note_found": "No se encontró ninguna nota",
|
"no_note_found": "No se encontró ninguna nota",
|
||||||
@ -98,7 +98,7 @@
|
|||||||
"do_you_wish_to_continue": "¿Deseas continuar?",
|
"do_you_wish_to_continue": "¿Deseas continuar?",
|
||||||
"note": "Nota",
|
"note": "Nota",
|
||||||
"pay_invoice": "Pagar factura",
|
"pay_invoice": "Pagar factura",
|
||||||
"login_successfully": "Logeado Satisfactoriamente!",
|
"login_successfully": "¡Sesión inciada con éxito!",
|
||||||
"logged_out_successfully": "Logeado Satisfactoriamente",
|
"logged_out_successfully": "Logeado Satisfactoriamente",
|
||||||
"mark_as_default": "Marcar como predeterminado"
|
"mark_as_default": "Marcar como predeterminado"
|
||||||
},
|
},
|
||||||
@ -113,7 +113,7 @@
|
|||||||
},
|
},
|
||||||
"chart_info": {
|
"chart_info": {
|
||||||
"total_sales": "Ventas",
|
"total_sales": "Ventas",
|
||||||
"total_receipts": "Ingresos",
|
"total_receipts": "Recibos",
|
||||||
"total_expense": "Gastos",
|
"total_expense": "Gastos",
|
||||||
"net_income": "Ingresos netos",
|
"net_income": "Ingresos netos",
|
||||||
"year": "Seleccione año"
|
"year": "Seleccione año"
|
||||||
@ -1485,7 +1485,7 @@
|
|||||||
"pdf_estimate_label": "Presupuestar",
|
"pdf_estimate_label": "Presupuestar",
|
||||||
"pdf_estimate_number": "Número de Presupuesto",
|
"pdf_estimate_number": "Número de Presupuesto",
|
||||||
"pdf_estimate_date": "Fecha presupuesto",
|
"pdf_estimate_date": "Fecha presupuesto",
|
||||||
"pdf_estimate_expire_date": "Fecha de caducidad",
|
"pdf_estimate_expire_date": "Fecha de vencimiento",
|
||||||
"pdf_invoice_label": "Factura",
|
"pdf_invoice_label": "Factura",
|
||||||
"pdf_invoice_number": "Numero de factura",
|
"pdf_invoice_number": "Numero de factura",
|
||||||
"pdf_invoice_date": "Fecha de la factura",
|
"pdf_invoice_date": "Fecha de la factura",
|
||||||
|
|||||||
@ -1485,7 +1485,7 @@
|
|||||||
"pdf_estimate_label": "Estimate",
|
"pdf_estimate_label": "Estimate",
|
||||||
"pdf_estimate_number": "Estimate Number",
|
"pdf_estimate_number": "Estimate Number",
|
||||||
"pdf_estimate_date": "Estimate Date",
|
"pdf_estimate_date": "Estimate Date",
|
||||||
"pdf_estimate_expire_date": "Expiry date",
|
"pdf_estimate_expire_date": "Expiry Date",
|
||||||
"pdf_invoice_label": "Invoice",
|
"pdf_invoice_label": "Invoice",
|
||||||
"pdf_invoice_number": "Invoice Number",
|
"pdf_invoice_number": "Invoice Number",
|
||||||
"pdf_invoice_date": "Invoice Date",
|
"pdf_invoice_date": "Invoice Date",
|
||||||
|
|||||||
@ -1485,7 +1485,7 @@
|
|||||||
"pdf_estimate_label": "Tarjous",
|
"pdf_estimate_label": "Tarjous",
|
||||||
"pdf_estimate_number": "Tarjousnumero",
|
"pdf_estimate_number": "Tarjousnumero",
|
||||||
"pdf_estimate_date": "Tarjouksen päiväys",
|
"pdf_estimate_date": "Tarjouksen päiväys",
|
||||||
"pdf_estimate_expire_date": "Voimassaolo päivä",
|
"pdf_estimate_expire_date": "Expiry Date",
|
||||||
"pdf_invoice_label": "Lasku",
|
"pdf_invoice_label": "Lasku",
|
||||||
"pdf_invoice_number": "Laskunumero",
|
"pdf_invoice_number": "Laskunumero",
|
||||||
"pdf_invoice_date": "Laskun päiväys",
|
"pdf_invoice_date": "Laskun päiväys",
|
||||||
|
|||||||
@ -310,7 +310,7 @@
|
|||||||
"confirm_mark_as_sent": "Ce devis sera marqué comme envoyé",
|
"confirm_mark_as_sent": "Ce devis sera marqué comme envoyé",
|
||||||
"confirm_mark_as_accepted": "Ce devis sera marqué comme accepté",
|
"confirm_mark_as_accepted": "Ce devis sera marqué comme accepté",
|
||||||
"confirm_mark_as_rejected": "Ce devis sera marqué comme rejeté",
|
"confirm_mark_as_rejected": "Ce devis sera marqué comme rejeté",
|
||||||
"no_matching_estimates": "Aucune estimation correspondante !",
|
"no_matching_estimates": "Aucun devis correspondant !",
|
||||||
"mark_as_sent_successfully": "Devis marqué comme envoyé",
|
"mark_as_sent_successfully": "Devis marqué comme envoyé",
|
||||||
"send_estimate_successfully": "Devis envoyé",
|
"send_estimate_successfully": "Devis envoyé",
|
||||||
"errors": {
|
"errors": {
|
||||||
@ -355,7 +355,7 @@
|
|||||||
"select_an_item": "Sélectionnez un article",
|
"select_an_item": "Sélectionnez un article",
|
||||||
"type_item_description": "Taper la description de l'article (facultatif)"
|
"type_item_description": "Taper la description de l'article (facultatif)"
|
||||||
},
|
},
|
||||||
"mark_as_default_estimate_template_description": "If enabled, the selected template will be automatically selected for new estimates."
|
"mark_as_default_estimate_template_description": "Si activé, le modèle sélectionné sera automatiquement utilisé pour les nouvelles estimations."
|
||||||
},
|
},
|
||||||
"invoices": {
|
"invoices": {
|
||||||
"title": "Factures",
|
"title": "Factures",
|
||||||
@ -447,7 +447,7 @@
|
|||||||
"marked_as_sent_message": "Facture supprimée | Factures supprimées",
|
"marked_as_sent_message": "Facture supprimée | Factures supprimées",
|
||||||
"something_went_wrong": "quelque chose a mal tourné",
|
"something_went_wrong": "quelque chose a mal tourné",
|
||||||
"invalid_due_amount_message": "Le paiement entré est supérieur au montant total dû pour cette facture. Veuillez vérifier et réessayer.",
|
"invalid_due_amount_message": "Le paiement entré est supérieur au montant total dû pour cette facture. Veuillez vérifier et réessayer.",
|
||||||
"mark_as_default_invoice_template_description": "If enabled, the selected template will be automatically selected for new invoices."
|
"mark_as_default_invoice_template_description": "Si activé, le modèle sélectionné sera automatiquement utilisé pour les nouvelles factures."
|
||||||
},
|
},
|
||||||
"recurring_invoices": {
|
"recurring_invoices": {
|
||||||
"title": "Factures récurrentes",
|
"title": "Factures récurrentes",
|
||||||
@ -526,7 +526,7 @@
|
|||||||
"cloned_successfully": "Facture récurrente clonée",
|
"cloned_successfully": "Facture récurrente clonée",
|
||||||
"clone_invoice": "Dupliquer",
|
"clone_invoice": "Dupliquer",
|
||||||
"confirm_clone": "Cette facture récurrente sera clonée dans une nouvelle facture récurrente",
|
"confirm_clone": "Cette facture récurrente sera clonée dans une nouvelle facture récurrente",
|
||||||
"add_customer_email": "Please add an email address for this customer to send invoices automatically.",
|
"add_customer_email": "Veuillez ajouter une adresse e-mail pour ce client afin d'envoyer les factures automatiquement.",
|
||||||
"item": {
|
"item": {
|
||||||
"title": "Nom",
|
"title": "Nom",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
@ -844,9 +844,9 @@
|
|||||||
"secret": "Secret",
|
"secret": "Secret",
|
||||||
"mailgun_secret": "Secret Mailgun",
|
"mailgun_secret": "Secret Mailgun",
|
||||||
"mailgun_domain": "Domaine",
|
"mailgun_domain": "Domaine",
|
||||||
"mailgun_endpoint": "Mailgun Endpoint",
|
"mailgun_endpoint": "Endpoint de Mailgun",
|
||||||
"ses_secret": "SES Secret",
|
"ses_secret": "Clé secrète SES",
|
||||||
"ses_key": "SES Key",
|
"ses_key": "Clé SES",
|
||||||
"password": "Mot de passe",
|
"password": "Mot de passe",
|
||||||
"username": "Nom d'utilisateur",
|
"username": "Nom d'utilisateur",
|
||||||
"mail_config": "Envoi d'emails",
|
"mail_config": "Envoi d'emails",
|
||||||
@ -1112,10 +1112,10 @@
|
|||||||
"error": "Vous ne pouvez pas supprimer le fournisseur actif",
|
"error": "Vous ne pouvez pas supprimer le fournisseur actif",
|
||||||
"default_currency_error": "Cette devise est déjà affectée à un fournisseur",
|
"default_currency_error": "Cette devise est déjà affectée à un fournisseur",
|
||||||
"exchange_help_text": "Veuillez entrer le taux de change pour convertir {currency} en {baseCurrency}",
|
"exchange_help_text": "Veuillez entrer le taux de change pour convertir {currency} en {baseCurrency}",
|
||||||
"currency_freak": "Currency Freak",
|
"currency_freak": "Currency Freaks",
|
||||||
"currency_layer": "Currency Layer",
|
"currency_layer": "Currency Layer",
|
||||||
"open_exchange_rate": "Open Exchange Rate",
|
"open_exchange_rate": "Ouvrir le taux de change",
|
||||||
"currency_converter": "Currency Converter",
|
"currency_converter": "Convertisseur de devises",
|
||||||
"server": "Serveur",
|
"server": "Serveur",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"active": "Actif",
|
"active": "Actif",
|
||||||
@ -1150,8 +1150,8 @@
|
|||||||
"payment_mode_added": "Mode de paiement ajouté",
|
"payment_mode_added": "Mode de paiement ajouté",
|
||||||
"payment_mode_updated": "Mode de paiement mis à jour",
|
"payment_mode_updated": "Mode de paiement mis à jour",
|
||||||
"payment_mode_confirm_delete": "Vous ne pourrez pas récupérer ce mode de paiement",
|
"payment_mode_confirm_delete": "Vous ne pourrez pas récupérer ce mode de paiement",
|
||||||
"payments_attached": "This payment method is already attached to payments. Please delete the attached payments to proceed with deletion.",
|
"payments_attached": "Ce moyen de paiement est déjà utilisé par des paiements. Merci de supprimer les paiements associés avant de procéder à la suppression.",
|
||||||
"expenses_attached": "This payment method is already attached to expenses. Please delete the attached expenses to proceed with deletion.",
|
"expenses_attached": "Ce moyen de paiement est déjà utilisé par des dépenses. Merci de supprimer les dépenses associés avant de procéder à la suppression.",
|
||||||
"deleted_message": "Mode de paiement supprimé"
|
"deleted_message": "Mode de paiement supprimé"
|
||||||
},
|
},
|
||||||
"expense_category": {
|
"expense_category": {
|
||||||
@ -1262,8 +1262,8 @@
|
|||||||
"media_driver": "Stockage multimédia",
|
"media_driver": "Stockage multimédia",
|
||||||
"media_root": "Répertoire média",
|
"media_root": "Répertoire média",
|
||||||
"aws_driver": "AWS",
|
"aws_driver": "AWS",
|
||||||
"aws_key": "AWS Key",
|
"aws_key": "Clé AWS",
|
||||||
"aws_secret": "AWS Secret",
|
"aws_secret": "Clé secrète AWS",
|
||||||
"aws_region": "Région AWS",
|
"aws_region": "Région AWS",
|
||||||
"aws_bucket": "Bucket",
|
"aws_bucket": "Bucket",
|
||||||
"aws_root": "Répertoire",
|
"aws_root": "Répertoire",
|
||||||
@ -1491,7 +1491,7 @@
|
|||||||
"pdf_invoice_date": "Date",
|
"pdf_invoice_date": "Date",
|
||||||
"pdf_invoice_due_date": "Date d’échéance",
|
"pdf_invoice_due_date": "Date d’échéance",
|
||||||
"pdf_notes": "Notes de bas de page",
|
"pdf_notes": "Notes de bas de page",
|
||||||
"pdf_items_label": "Articles",
|
"pdf_items_label": "Désignation",
|
||||||
"pdf_quantity_label": "Quantité",
|
"pdf_quantity_label": "Quantité",
|
||||||
"pdf_price_label": "Prix",
|
"pdf_price_label": "Prix",
|
||||||
"pdf_discount_label": "Remise",
|
"pdf_discount_label": "Remise",
|
||||||
|
|||||||
@ -510,11 +510,11 @@
|
|||||||
"update_expense": "Update Expense",
|
"update_expense": "Update Expense",
|
||||||
"edit_invoice": "Edit Recurring Invoice",
|
"edit_invoice": "Edit Recurring Invoice",
|
||||||
"new_invoice": "New Recurring Invoice",
|
"new_invoice": "New Recurring Invoice",
|
||||||
"send_automatically": "Send Automatically",
|
"send_automatically": "अटोमेटिक",
|
||||||
"send_automatically_desc": "Enable this, if you would like to send the invoice automatically to the customer when its created.",
|
"send_automatically_desc": "Enable this, if you would like to send the invoice automatically to the customer when its created.",
|
||||||
"save_invoice": "Save Recurring Invoice",
|
"save_invoice": "Save Recurring Invoice",
|
||||||
"update_invoice": "Update Recurring Invoice",
|
"update_invoice": "Update Recurring Invoice",
|
||||||
"add_new_tax": "Add New Tax",
|
"add_new_tax": "नवी कर",
|
||||||
"no_invoices": "No Recurring Invoices yet!",
|
"no_invoices": "No Recurring Invoices yet!",
|
||||||
"mark_as_rejected": "Mark as rejected",
|
"mark_as_rejected": "Mark as rejected",
|
||||||
"mark_as_accepted": "Mark as accepted",
|
"mark_as_accepted": "Mark as accepted",
|
||||||
@ -1485,7 +1485,7 @@
|
|||||||
"pdf_estimate_label": "Estimate",
|
"pdf_estimate_label": "Estimate",
|
||||||
"pdf_estimate_number": "Estimate Number",
|
"pdf_estimate_number": "Estimate Number",
|
||||||
"pdf_estimate_date": "Estimate Date",
|
"pdf_estimate_date": "Estimate Date",
|
||||||
"pdf_estimate_expire_date": "Expiry date",
|
"pdf_estimate_expire_date": "Expiry Date",
|
||||||
"pdf_invoice_label": "Invoice",
|
"pdf_invoice_label": "Invoice",
|
||||||
"pdf_invoice_number": "Invoice Number",
|
"pdf_invoice_number": "Invoice Number",
|
||||||
"pdf_invoice_date": "Invoice Date",
|
"pdf_invoice_date": "Invoice Date",
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
"customers": "Klijenti",
|
"customers": "Klijenti",
|
||||||
"items": "Stavke",
|
"items": "Stavke",
|
||||||
"invoices": "Fakture",
|
"invoices": "Fakture",
|
||||||
"recurring-invoices": "Recurring Invoices",
|
"recurring-invoices": "Ponavljajuće fakture\n",
|
||||||
"expenses": "Rashodi",
|
"expenses": "Rashodi",
|
||||||
"estimates": "Ponude",
|
"estimates": "Ponude",
|
||||||
"payments": "Uplate",
|
"payments": "Uplate",
|
||||||
@ -12,7 +12,7 @@
|
|||||||
"settings": "Postavke",
|
"settings": "Postavke",
|
||||||
"logout": "Odjava",
|
"logout": "Odjava",
|
||||||
"users": "Korisnici",
|
"users": "Korisnici",
|
||||||
"modules": "Modules"
|
"modules": "Moduli"
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"add_company": "Dodaj tvrtku",
|
"add_company": "Dodaj tvrtku",
|
||||||
@ -30,8 +30,8 @@
|
|||||||
"from": "Pošiljatelj",
|
"from": "Pošiljatelj",
|
||||||
"to": "Primatelj",
|
"to": "Primatelj",
|
||||||
"ok": "Ok",
|
"ok": "Ok",
|
||||||
"yes": "Yes",
|
"yes": "Da",
|
||||||
"no": "No",
|
"no": "Ne",
|
||||||
"sort_by": "Posloži Po",
|
"sort_by": "Posloži Po",
|
||||||
"ascending": "Rastuće",
|
"ascending": "Rastuće",
|
||||||
"descending": "Padajuće",
|
"descending": "Padajuće",
|
||||||
@ -39,7 +39,7 @@
|
|||||||
"body": "Tijelo",
|
"body": "Tijelo",
|
||||||
"message": "Poruka",
|
"message": "Poruka",
|
||||||
"send": "Pošalji",
|
"send": "Pošalji",
|
||||||
"preview": "Preview",
|
"preview": "Pregled",
|
||||||
"go_back": "Natrag",
|
"go_back": "Natrag",
|
||||||
"back_to_login": "Natrag na prijavu?",
|
"back_to_login": "Natrag na prijavu?",
|
||||||
"home": "Početna",
|
"home": "Početna",
|
||||||
@ -65,7 +65,7 @@
|
|||||||
"sent": "Poslano",
|
"sent": "Poslano",
|
||||||
"all": "Sve",
|
"all": "Sve",
|
||||||
"select_all": "Izaberi sve",
|
"select_all": "Izaberi sve",
|
||||||
"select_template": "Select Template",
|
"select_template": "Odaberite predložak",
|
||||||
"choose_file": "Klikni ovdje da izabereš fajl",
|
"choose_file": "Klikni ovdje da izabereš fajl",
|
||||||
"choose_template": "Izaberi predložak",
|
"choose_template": "Izaberi predložak",
|
||||||
"choose": "Izaberi",
|
"choose": "Izaberi",
|
||||||
@ -93,13 +93,13 @@
|
|||||||
"no_note_found": "Ne postoje spremljene napomene",
|
"no_note_found": "Ne postoje spremljene napomene",
|
||||||
"insert_note": "Unesi bilješku",
|
"insert_note": "Unesi bilješku",
|
||||||
"copied_pdf_url_clipboard": "Link do PDF fajla kopiran!",
|
"copied_pdf_url_clipboard": "Link do PDF fajla kopiran!",
|
||||||
"copied_url_clipboard": "Copied url to clipboard!",
|
"copied_url_clipboard": "URL je kopiran u međuspremnik!",
|
||||||
"docs": "Docs",
|
"docs": "Dokumenti",
|
||||||
"do_you_wish_to_continue": "Do you wish to continue?",
|
"do_you_wish_to_continue": "Želite li nastaviti?",
|
||||||
"note": "Note",
|
"note": "Bilješka",
|
||||||
"pay_invoice": "Pay Invoice",
|
"pay_invoice": "Plati račun",
|
||||||
"login_successfully": "Logged in successfully!",
|
"login_successfully": "Uspješno ste prijavljeni!\n",
|
||||||
"logged_out_successfully": "Logged out successfully",
|
"logged_out_successfully": "Uspješno odjavljen\n",
|
||||||
"mark_as_default": "Postavi kao zadano"
|
"mark_as_default": "Postavi kao zadano"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
@ -109,7 +109,7 @@
|
|||||||
"customers": "Klijenti",
|
"customers": "Klijenti",
|
||||||
"invoices": "Računi",
|
"invoices": "Računi",
|
||||||
"estimates": "Ponude",
|
"estimates": "Ponude",
|
||||||
"payments": "Payments"
|
"payments": "Plaćanja"
|
||||||
},
|
},
|
||||||
"chart_info": {
|
"chart_info": {
|
||||||
"total_sales": "Prodaja",
|
"total_sales": "Prodaja",
|
||||||
@ -151,18 +151,18 @@
|
|||||||
"no_results_found": "Nema rezultata"
|
"no_results_found": "Nema rezultata"
|
||||||
},
|
},
|
||||||
"company_switcher": {
|
"company_switcher": {
|
||||||
"label": "SWITCH COMPANY",
|
"label": "IZABERI TVRTKU",
|
||||||
"no_results_found": "No Results Found",
|
"no_results_found": "Nema rezultata\n",
|
||||||
"add_new_company": "Add new company",
|
"add_new_company": "Dodajte novu tvrtku\n",
|
||||||
"new_company": "New company",
|
"new_company": "Nova tvrtka\n",
|
||||||
"created_message": "Company created successfully"
|
"created_message": "Tvrtka uspješno stvorena\n"
|
||||||
},
|
},
|
||||||
"dateRange": {
|
"dateRange": {
|
||||||
"today": "Today",
|
"today": "Danas\n",
|
||||||
"this_week": "This Week",
|
"this_week": "Danas\n",
|
||||||
"this_month": "This Month",
|
"this_month": "Danas\n",
|
||||||
"this_quarter": "This Quarter",
|
"this_quarter": "Danas\n",
|
||||||
"this_year": "This Year",
|
"this_year": "Danas\n",
|
||||||
"previous_week": "Previous Week",
|
"previous_week": "Previous Week",
|
||||||
"previous_month": "Previous Month",
|
"previous_month": "Previous Month",
|
||||||
"previous_quarter": "Previous Quarter",
|
"previous_quarter": "Previous Quarter",
|
||||||
@ -456,32 +456,32 @@
|
|||||||
"months": "{months} Month",
|
"months": "{months} Month",
|
||||||
"years": "{years} Year",
|
"years": "{years} Year",
|
||||||
"all": "All",
|
"all": "All",
|
||||||
"paid": "Paid",
|
"paid": "Plaćeno",
|
||||||
"unpaid": "Unpaid",
|
"unpaid": "Ne plaćeno",
|
||||||
"viewed": "Viewed",
|
"viewed": "Pregledano",
|
||||||
"overdue": "Overdue",
|
"overdue": "Kašnjenja",
|
||||||
"active": "Active",
|
"active": "Aktivno",
|
||||||
"completed": "Completed",
|
"completed": "Završeno",
|
||||||
"customer": "CUSTOMER",
|
"customer": "KUPAC",
|
||||||
"paid_status": "PAID STATUS",
|
"paid_status": "STATUS PLAĆANJA",
|
||||||
"ref_no": "REF NO.",
|
"ref_no": "BR. REFERENCE",
|
||||||
"number": "NUMBER",
|
"number": "BROJ",
|
||||||
"amount_due": "AMOUNT DUE",
|
"amount_due": "AMOUNT DUE",
|
||||||
"partially_paid": "Partially Paid",
|
"partially_paid": "Djelomično plaćeno\n",
|
||||||
"total": "Total",
|
"total": "Ukupno",
|
||||||
"discount": "Discount",
|
"discount": "Popust",
|
||||||
"sub_total": "Sub Total",
|
"sub_total": "Popdzbroj",
|
||||||
"invoice": "Recurring Invoice | Recurring Invoices",
|
"invoice": "Ponavljajuća faktura | Ponavjaljuća faktura",
|
||||||
"invoice_number": "Recurring Invoice Number",
|
"invoice_number": "Broj ponavljajuće fakture\n",
|
||||||
"next_invoice_date": "Next Invoice Date",
|
"next_invoice_date": "Datum sljedećeg računa",
|
||||||
"ref_number": "Ref Number",
|
"ref_number": "Broj reference",
|
||||||
"contact": "Contact",
|
"contact": "Kontakt",
|
||||||
"add_item": "Add an Item",
|
"add_item": "Dodaj stavku",
|
||||||
"date": "Date",
|
"date": "Datum",
|
||||||
"limit_by": "Limit by",
|
"limit_by": "Ograniči po",
|
||||||
"limit_date": "Limit Date",
|
"limit_date": "Vremenski rok",
|
||||||
"limit_count": "Limit Count",
|
"limit_count": "Broj ograničenja\n",
|
||||||
"count": "Count",
|
"count": "Brojač",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"select_a_status": "Select a status",
|
"select_a_status": "Select a status",
|
||||||
"working": "Working",
|
"working": "Working",
|
||||||
@ -1485,7 +1485,7 @@
|
|||||||
"pdf_estimate_label": "Ponuda",
|
"pdf_estimate_label": "Ponuda",
|
||||||
"pdf_estimate_number": "Broj Ponude",
|
"pdf_estimate_number": "Broj Ponude",
|
||||||
"pdf_estimate_date": "Datum Ponude",
|
"pdf_estimate_date": "Datum Ponude",
|
||||||
"pdf_estimate_expire_date": "Datum isteka Ponude",
|
"pdf_estimate_expire_date": "Expiry Date",
|
||||||
"pdf_invoice_label": "Faktura",
|
"pdf_invoice_label": "Faktura",
|
||||||
"pdf_invoice_number": "Broj Fakture",
|
"pdf_invoice_number": "Broj Fakture",
|
||||||
"pdf_invoice_date": "Datum Fakture",
|
"pdf_invoice_date": "Datum Fakture",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -54,9 +54,9 @@
|
|||||||
"actions": "Azioni",
|
"actions": "Azioni",
|
||||||
"subtotal": "SUBTOTALE",
|
"subtotal": "SUBTOTALE",
|
||||||
"discount": "SCONTO",
|
"discount": "SCONTO",
|
||||||
"fixed": "Fissato",
|
"fixed": "Fisso",
|
||||||
"percentage": "Percentuale",
|
"percentage": "Percentuale",
|
||||||
"tax": "TASSA",
|
"tax": "IMPOSTA",
|
||||||
"total_amount": "AMMONTARE TOTALE",
|
"total_amount": "AMMONTARE TOTALE",
|
||||||
"bill_to": "Fattura a",
|
"bill_to": "Fattura a",
|
||||||
"ship_to": "Invia a",
|
"ship_to": "Invia a",
|
||||||
|
|||||||
@ -1485,7 +1485,7 @@
|
|||||||
"pdf_estimate_label": "Estimate",
|
"pdf_estimate_label": "Estimate",
|
||||||
"pdf_estimate_number": "Estimate Number",
|
"pdf_estimate_number": "Estimate Number",
|
||||||
"pdf_estimate_date": "Estimate Date",
|
"pdf_estimate_date": "Estimate Date",
|
||||||
"pdf_estimate_expire_date": "Expiry date",
|
"pdf_estimate_expire_date": "Expiry Date",
|
||||||
"pdf_invoice_label": "Invoice",
|
"pdf_invoice_label": "Invoice",
|
||||||
"pdf_invoice_number": "Invoice Number",
|
"pdf_invoice_number": "Invoice Number",
|
||||||
"pdf_invoice_date": "Invoice Date",
|
"pdf_invoice_date": "Invoice Date",
|
||||||
|
|||||||
@ -17,7 +17,6 @@ import sk from './sk.json'
|
|||||||
import vi from './vi.json'
|
import vi from './vi.json'
|
||||||
import el from './el.json'
|
import el from './el.json'
|
||||||
import hr from './hr.json'
|
import hr from './hr.json'
|
||||||
import th from './th.json'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
cs,
|
cs,
|
||||||
@ -38,6 +37,5 @@ export default {
|
|||||||
vi,
|
vi,
|
||||||
pl,
|
pl,
|
||||||
el,
|
el,
|
||||||
hr,
|
hr
|
||||||
th
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1485,7 +1485,7 @@
|
|||||||
"pdf_estimate_label": "Pasiūlymas",
|
"pdf_estimate_label": "Pasiūlymas",
|
||||||
"pdf_estimate_number": "Pasiūlymo numeris",
|
"pdf_estimate_number": "Pasiūlymo numeris",
|
||||||
"pdf_estimate_date": "Pasiūlymo data",
|
"pdf_estimate_date": "Pasiūlymo data",
|
||||||
"pdf_estimate_expire_date": "Galiojimo laikas",
|
"pdf_estimate_expire_date": "Expiry Date",
|
||||||
"pdf_invoice_label": "Sąskaita",
|
"pdf_invoice_label": "Sąskaita",
|
||||||
"pdf_invoice_number": "Invoice Number",
|
"pdf_invoice_number": "Invoice Number",
|
||||||
"pdf_invoice_date": "Invoice Date",
|
"pdf_invoice_date": "Invoice Date",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@
|
|||||||
"customers": "Klanten",
|
"customers": "Klanten",
|
||||||
"items": "Artikelen",
|
"items": "Artikelen",
|
||||||
"invoices": "Facturen",
|
"invoices": "Facturen",
|
||||||
"recurring-invoices": "Periodieke factuur",
|
"recurring-invoices": "Periodieke facturen",
|
||||||
"expenses": "Uitgaven",
|
"expenses": "Uitgaven",
|
||||||
"estimates": "Offertes",
|
"estimates": "Offertes",
|
||||||
"payments": "Betalingen",
|
"payments": "Betalingen",
|
||||||
@ -28,16 +28,16 @@
|
|||||||
"from_date": "Vanaf datum",
|
"from_date": "Vanaf datum",
|
||||||
"to_date": "T/m datum",
|
"to_date": "T/m datum",
|
||||||
"from": "Vanaf",
|
"from": "Vanaf",
|
||||||
"to": "Naar.",
|
"to": "Naar",
|
||||||
"ok": "Oké.",
|
"ok": "Oké",
|
||||||
"yes": "Ja.",
|
"yes": "Ja",
|
||||||
"no": "Nee.",
|
"no": "Nee",
|
||||||
"sort_by": "Sorteer op",
|
"sort_by": "Sorteer op",
|
||||||
"ascending": "Oplopend",
|
"ascending": "Oplopend",
|
||||||
"descending": "Aflopend",
|
"descending": "Aflopend",
|
||||||
"subject": "Onderwerp",
|
"subject": "Onderwerp",
|
||||||
"body": "Inhoud",
|
"body": "Inhoud",
|
||||||
"message": "Bericht.",
|
"message": "Bericht",
|
||||||
"send": "Verstuur",
|
"send": "Verstuur",
|
||||||
"preview": "Voorbeeld",
|
"preview": "Voorbeeld",
|
||||||
"go_back": "Ga terug",
|
"go_back": "Ga terug",
|
||||||
@ -54,7 +54,7 @@
|
|||||||
"actions": "Acties",
|
"actions": "Acties",
|
||||||
"subtotal": "SUBTOTAAL",
|
"subtotal": "SUBTOTAAL",
|
||||||
"discount": "KORTING",
|
"discount": "KORTING",
|
||||||
"fixed": "Gemaakt",
|
"fixed": "Vast bedrag",
|
||||||
"percentage": "Percentage",
|
"percentage": "Percentage",
|
||||||
"tax": "BELASTING",
|
"tax": "BELASTING",
|
||||||
"total_amount": "TOTAALBEDRAG",
|
"total_amount": "TOTAALBEDRAG",
|
||||||
@ -85,17 +85,17 @@
|
|||||||
"select_state": "Selecteer staat",
|
"select_state": "Selecteer staat",
|
||||||
"select_country": "Selecteer land",
|
"select_country": "Selecteer land",
|
||||||
"select_city": "Selecteer stad",
|
"select_city": "Selecteer stad",
|
||||||
"street_1": "straat 1",
|
"street_1": "Straat 1",
|
||||||
"street_2": "Straat # 2",
|
"street_2": "Straat 2",
|
||||||
"action_failed": "Actie: mislukt",
|
"action_failed": "Actie mislukt",
|
||||||
"retry": "Retr",
|
"retry": "Opnieuw proberen",
|
||||||
"choose_note": "Kies notitie",
|
"choose_note": "Kies notitie",
|
||||||
"no_note_found": "Geen notitie gevonden",
|
"no_note_found": "Geen notitie gevonden",
|
||||||
"insert_note": "Notitie invoegen",
|
"insert_note": "Notitie invoegen",
|
||||||
"copied_pdf_url_clipboard": "PDF link naar klembord gekopieerd!",
|
"copied_pdf_url_clipboard": "PDF link naar klembord gekopieerd!",
|
||||||
"copied_url_clipboard": "URL naar klembord gekopieerd!",
|
"copied_url_clipboard": "URL naar klembord gekopieerd!",
|
||||||
"docs": "Documenten",
|
"docs": "Documentatie",
|
||||||
"do_you_wish_to_continue": "Wilt u Doorgaan?",
|
"do_you_wish_to_continue": "Wil je doorgaan?",
|
||||||
"note": "Notitie",
|
"note": "Notitie",
|
||||||
"pay_invoice": "Betaal factuur",
|
"pay_invoice": "Betaal factuur",
|
||||||
"login_successfully": "Succesvol ingelogd!",
|
"login_successfully": "Succesvol ingelogd!",
|
||||||
@ -526,7 +526,7 @@
|
|||||||
"cloned_successfully": "Terugkerende factuur succesvol gekopieerd",
|
"cloned_successfully": "Terugkerende factuur succesvol gekopieerd",
|
||||||
"clone_invoice": "Kopieer periodieke factuur",
|
"clone_invoice": "Kopieer periodieke factuur",
|
||||||
"confirm_clone": "Deze periodieke factuur wordt gekopieerd naar een nieuwe periodieke factuur",
|
"confirm_clone": "Deze periodieke factuur wordt gekopieerd naar een nieuwe periodieke factuur",
|
||||||
"add_customer_email": "Voeg een e-mailadres aan deze klant toe, zodat facturen automatisch verzonden kunnen worden.",
|
"add_customer_email": "Voeg een e-mailadres aan deze klant toe zodat facturen automatisch verzonden kunnen worden.",
|
||||||
"item": {
|
"item": {
|
||||||
"title": "Item titel",
|
"title": "Item titel",
|
||||||
"description": "Beschrijving",
|
"description": "Beschrijving",
|
||||||
@ -1114,13 +1114,13 @@
|
|||||||
"exchange_help_text": "Voer de wisselkoers in om te converteren van {currency} naar {baseCurrency}",
|
"exchange_help_text": "Voer de wisselkoers in om te converteren van {currency} naar {baseCurrency}",
|
||||||
"currency_freak": "Valuta Freak",
|
"currency_freak": "Valuta Freak",
|
||||||
"currency_layer": "Valuta-laag",
|
"currency_layer": "Valuta-laag",
|
||||||
"open_exchange_rate": "Open Exchange Rate",
|
"open_exchange_rate": "Open wisselkoers",
|
||||||
"currency_converter": "Valuta omzetter",
|
"currency_converter": "Valuta omzetter",
|
||||||
"server": "Server",
|
"server": "Server",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
"active": "Actief",
|
"active": "Actief",
|
||||||
"currency_help_text": "This provider will only be used on above selected currencies",
|
"currency_help_text": "Deze aanbieder wordt alleen gebruikt voor de hier boven geselecteerde valuta",
|
||||||
"currency_in_used": "The following currencies are already active on another provider. Please remove these currencies from selection to activate this provider again."
|
"currency_in_used": "De volgende valuta zijn al actief bij een andere aanbieder. Verwijder deze valuta uit de selectie om deze aanbieder opnieuw te activeren."
|
||||||
},
|
},
|
||||||
"tax_types": {
|
"tax_types": {
|
||||||
"title": "Belastingtypen",
|
"title": "Belastingtypen",
|
||||||
@ -1143,16 +1143,16 @@
|
|||||||
},
|
},
|
||||||
"payment_modes": {
|
"payment_modes": {
|
||||||
"title": "Betaalmethodes",
|
"title": "Betaalmethodes",
|
||||||
"description": "Modes of transaction for payments",
|
"description": "Modi van transacties voor betalingen",
|
||||||
"add_payment_mode": "Add Payment Mode",
|
"add_payment_mode": "Voeg betaalwijze toe",
|
||||||
"edit_payment_mode": "Edit Payment Mode",
|
"edit_payment_mode": "Wijzig betaalwijze",
|
||||||
"mode_name": "Mode Name",
|
"mode_name": "Modusnaam",
|
||||||
"payment_mode_added": "Betaalwijze toegevoegd",
|
"payment_mode_added": "Betaalwijze toegevoegd",
|
||||||
"payment_mode_updated": "Payment Mode Updated",
|
"payment_mode_updated": "Betaalwijze bijgewerkt",
|
||||||
"payment_mode_confirm_delete": "You will not be able to recover this Payment Mode",
|
"payment_mode_confirm_delete": "U kunt deze betalingsmodus niet herstellen",
|
||||||
"payments_attached": "This payment method is already attached to payments. Please delete the attached payments to proceed with deletion.",
|
"payments_attached": "Deze betalingsmethode is al gekoppeld aan betalingen. Verwijder de gekoppelde betalingen om verder te gaan met het verwijderen.",
|
||||||
"expenses_attached": "This payment method is already attached to expenses. Please delete the attached expenses to proceed with deletion.",
|
"expenses_attached": "Deze betaalmethode is al gekoppeld aan uitgaven. Verwijder de gekoppelde kosten om door te gaan met het verwijderen.",
|
||||||
"deleted_message": "Payment Mode deleted successfully"
|
"deleted_message": "Betaalwijze succesvol verwijderd"
|
||||||
},
|
},
|
||||||
"expense_category": {
|
"expense_category": {
|
||||||
"title": "Onkostencategorieën",
|
"title": "Onkostencategorieën",
|
||||||
@ -1178,8 +1178,8 @@
|
|||||||
"discount_setting": "Kortingsinstelling",
|
"discount_setting": "Kortingsinstelling",
|
||||||
"discount_per_item": "Korting per item",
|
"discount_per_item": "Korting per item",
|
||||||
"discount_setting_description": "Schakel dit in als u korting wilt toevoegen aan afzonderlijke factuuritems. Standaard wordt korting rechtstreeks aan de factuur toegevoegd.",
|
"discount_setting_description": "Schakel dit in als u korting wilt toevoegen aan afzonderlijke factuuritems. Standaard wordt korting rechtstreeks aan de factuur toegevoegd.",
|
||||||
"expire_public_links": "Automatically Expire Public Links",
|
"expire_public_links": "Publieke links automatisch laten vervallen",
|
||||||
"expire_setting_description": "Specify whether you would like to expire all the links sent by application to view invoices, estimates & payments, etc after a specified duration.",
|
"expire_setting_description": "Geef aan of je alle links die eerder zijn verstuurd door de applicatie om facturen, offertes en betalingen te bekijken wilt laten verlopen na een bepaalde duur.",
|
||||||
"save": "Opslaan",
|
"save": "Opslaan",
|
||||||
"preference": "Voorkeur | Voorkeuren",
|
"preference": "Voorkeur | Voorkeuren",
|
||||||
"general_settings": "Standaardvoorkeuren voor het systeem.",
|
"general_settings": "Standaardvoorkeuren voor het systeem.",
|
||||||
@ -1188,9 +1188,9 @@
|
|||||||
"select_time_zone": "Selecteer Tijdzone",
|
"select_time_zone": "Selecteer Tijdzone",
|
||||||
"select_date_format": "Selecteer datum/tijdindeling",
|
"select_date_format": "Selecteer datum/tijdindeling",
|
||||||
"select_financial_year": "Selecteer financieel ja",
|
"select_financial_year": "Selecteer financieel ja",
|
||||||
"recurring_invoice_status": "Recurring Invoice Status",
|
"recurring_invoice_status": "Status periodieke facturen",
|
||||||
"create_status": "Create Status",
|
"create_status": "Status aanmaken",
|
||||||
"active": "Active",
|
"active": "Actief",
|
||||||
"on_hold": "In wacht",
|
"on_hold": "In wacht",
|
||||||
"update_status": "Updatestatus",
|
"update_status": "Updatestatus",
|
||||||
"completed": "Voltooid",
|
"completed": "Voltooid",
|
||||||
@ -1217,10 +1217,10 @@
|
|||||||
"finishing_update": "Afwerking Update",
|
"finishing_update": "Afwerking Update",
|
||||||
"update_failed": "Update mislukt",
|
"update_failed": "Update mislukt",
|
||||||
"update_failed_text": "Sorry! Je update is mislukt op: {step} step ",
|
"update_failed_text": "Sorry! Je update is mislukt op: {step} step ",
|
||||||
"update_warning": "All of the application files and default template files will be overwritten when you update the application using this utility. Please take a backup of your templates & database before updating."
|
"update_warning": "Alle applicatiebestanden en de standaard sjabloonbestanden worden overschreven wanneer u de applicatie met behulp van dit hulpprogramma bijwerkt. Maak een reservekopie van uw sjablonen en database voordat u deze bijwerkt."
|
||||||
},
|
},
|
||||||
"backup": {
|
"backup": {
|
||||||
"title": "Backup | Backups",
|
"title": "Back-up | Back-ups",
|
||||||
"description": "De back-up is een zipfile met alle bestanden in de mappen die je opgeeft samen met een dump van je database",
|
"description": "De back-up is een zipfile met alle bestanden in de mappen die je opgeeft samen met een dump van je database",
|
||||||
"new_backup": "Nieuwe back-up",
|
"new_backup": "Nieuwe back-up",
|
||||||
"create_backup": "Backup maken",
|
"create_backup": "Backup maken",
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
"navigation": {
|
"navigation": {
|
||||||
"dashboard": "Pulpit",
|
"dashboard": "Pulpit",
|
||||||
"customers": "Kontrahenci",
|
"customers": "Kontrahenci",
|
||||||
"items": "Pozycje",
|
"items": "Produkty",
|
||||||
"invoices": "Faktury",
|
"invoices": "Faktury",
|
||||||
"recurring-invoices": "Faktury cykliczne",
|
"recurring-invoices": "Faktury cykliczne",
|
||||||
"expenses": "Wydatki",
|
"expenses": "Wydatki",
|
||||||
@ -100,7 +100,7 @@
|
|||||||
"pay_invoice": "Zapłać Fakturę",
|
"pay_invoice": "Zapłać Fakturę",
|
||||||
"login_successfully": "Zalogowano pomyślnie!",
|
"login_successfully": "Zalogowano pomyślnie!",
|
||||||
"logged_out_successfully": "Wylogowano pomyślnie",
|
"logged_out_successfully": "Wylogowano pomyślnie",
|
||||||
"mark_as_default": "Mark as default"
|
"mark_as_default": "Oznacz jako domyślne"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"select_year": "Wybierz rok",
|
"select_year": "Wybierz rok",
|
||||||
@ -355,7 +355,7 @@
|
|||||||
"select_an_item": "Wpisz lub kliknij aby wybrać element",
|
"select_an_item": "Wpisz lub kliknij aby wybrać element",
|
||||||
"type_item_description": "Opis pozycji (opcjonalnie)"
|
"type_item_description": "Opis pozycji (opcjonalnie)"
|
||||||
},
|
},
|
||||||
"mark_as_default_estimate_template_description": "If enabled, the selected template will be automatically selected for new estimates."
|
"mark_as_default_estimate_template_description": "Jeśli włączone, wybrany szablon zostanie automatycznie wybrany dla nowych ofert."
|
||||||
},
|
},
|
||||||
"invoices": {
|
"invoices": {
|
||||||
"title": "Faktury",
|
"title": "Faktury",
|
||||||
@ -447,7 +447,7 @@
|
|||||||
"marked_as_sent_message": "Faktura oznaczona jako wysłana pomyślnie",
|
"marked_as_sent_message": "Faktura oznaczona jako wysłana pomyślnie",
|
||||||
"something_went_wrong": "coś poszło nie tak",
|
"something_went_wrong": "coś poszło nie tak",
|
||||||
"invalid_due_amount_message": "Całkowita kwota faktury nie może być mniejsza niż całkowita kwota zapłacona za tę fakturę. Proszę zaktualizować fakturę lub usunąć powiązane płatności, aby kontynuować.",
|
"invalid_due_amount_message": "Całkowita kwota faktury nie może być mniejsza niż całkowita kwota zapłacona za tę fakturę. Proszę zaktualizować fakturę lub usunąć powiązane płatności, aby kontynuować.",
|
||||||
"mark_as_default_invoice_template_description": "If enabled, the selected template will be automatically selected for new invoices."
|
"mark_as_default_invoice_template_description": "Jeśli włączone, wybrany szablon zostanie automatycznie wybrany dla nowych faktur."
|
||||||
},
|
},
|
||||||
"recurring_invoices": {
|
"recurring_invoices": {
|
||||||
"title": "Faktury cykliczne",
|
"title": "Faktury cykliczne",
|
||||||
@ -526,7 +526,7 @@
|
|||||||
"cloned_successfully": "Faktura Cykliczna sklonowana pomyślnie",
|
"cloned_successfully": "Faktura Cykliczna sklonowana pomyślnie",
|
||||||
"clone_invoice": "Klonuj Fakturę Cykliczną",
|
"clone_invoice": "Klonuj Fakturę Cykliczną",
|
||||||
"confirm_clone": "Ta faktura cykliczna zostanie sklonowana do nowej faktury cyklicznej",
|
"confirm_clone": "Ta faktura cykliczna zostanie sklonowana do nowej faktury cyklicznej",
|
||||||
"add_customer_email": "Please add an email address for this customer to send invoices automatically.",
|
"add_customer_email": "Dodaj adres e-mail dla tego klienta, aby wysyłać faktury automatycznie.",
|
||||||
"item": {
|
"item": {
|
||||||
"title": "Tytuł pozycji",
|
"title": "Tytuł pozycji",
|
||||||
"description": "Opis",
|
"description": "Opis",
|
||||||
@ -1485,7 +1485,7 @@
|
|||||||
"pdf_estimate_label": "Oferta",
|
"pdf_estimate_label": "Oferta",
|
||||||
"pdf_estimate_number": "Numer oferty",
|
"pdf_estimate_number": "Numer oferty",
|
||||||
"pdf_estimate_date": "Data oferty",
|
"pdf_estimate_date": "Data oferty",
|
||||||
"pdf_estimate_expire_date": "Termin ważności",
|
"pdf_estimate_expire_date": "Data ważności",
|
||||||
"pdf_invoice_label": "Faktura",
|
"pdf_invoice_label": "Faktura",
|
||||||
"pdf_invoice_number": "Numer faktury",
|
"pdf_invoice_number": "Numer faktury",
|
||||||
"pdf_invoice_date": "Data faktury",
|
"pdf_invoice_date": "Data faktury",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -97,8 +97,8 @@
|
|||||||
"docs": "Documentație",
|
"docs": "Documentație",
|
||||||
"do_you_wish_to_continue": "Doriţi să continuaţi?",
|
"do_you_wish_to_continue": "Doriţi să continuaţi?",
|
||||||
"note": "Note",
|
"note": "Note",
|
||||||
"pay_invoice": "Pay Invoice",
|
"pay_invoice": "Plătește factura",
|
||||||
"login_successfully": "Logged in successfully!",
|
"login_successfully": "Autentificat cu succes!",
|
||||||
"logged_out_successfully": "Logged out successfully",
|
"logged_out_successfully": "Logged out successfully",
|
||||||
"mark_as_default": "Mark as default"
|
"mark_as_default": "Mark as default"
|
||||||
},
|
},
|
||||||
@ -1485,7 +1485,7 @@
|
|||||||
"pdf_estimate_label": "Estimate",
|
"pdf_estimate_label": "Estimate",
|
||||||
"pdf_estimate_number": "Estimate Number",
|
"pdf_estimate_number": "Estimate Number",
|
||||||
"pdf_estimate_date": "Estimate Date",
|
"pdf_estimate_date": "Estimate Date",
|
||||||
"pdf_estimate_expire_date": "Expiry date",
|
"pdf_estimate_expire_date": "Expiry Date",
|
||||||
"pdf_invoice_label": "Invoice",
|
"pdf_invoice_label": "Invoice",
|
||||||
"pdf_invoice_number": "Invoice Number",
|
"pdf_invoice_number": "Invoice Number",
|
||||||
"pdf_invoice_date": "Invoice Date",
|
"pdf_invoice_date": "Invoice Date",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,7 @@
|
|||||||
"customers": "Zákazníci",
|
"customers": "Zákazníci",
|
||||||
"items": "Položky",
|
"items": "Položky",
|
||||||
"invoices": "Faktúry",
|
"invoices": "Faktúry",
|
||||||
"recurring-invoices": "Recurring Invoices",
|
"recurring-invoices": "Pravidelné faktúry",
|
||||||
"expenses": "Výdaje",
|
"expenses": "Výdaje",
|
||||||
"estimates": "Cenové odhady",
|
"estimates": "Cenové odhady",
|
||||||
"payments": "Platby",
|
"payments": "Platby",
|
||||||
@ -12,7 +12,7 @@
|
|||||||
"settings": "Nastavenia",
|
"settings": "Nastavenia",
|
||||||
"logout": "Odhlásiť sa",
|
"logout": "Odhlásiť sa",
|
||||||
"users": "Uživatelia",
|
"users": "Uživatelia",
|
||||||
"modules": "Modules"
|
"modules": "Moduly"
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"add_company": "Pridať firmu",
|
"add_company": "Pridať firmu",
|
||||||
@ -30,8 +30,8 @@
|
|||||||
"from": "Od",
|
"from": "Od",
|
||||||
"to": "Pre",
|
"to": "Pre",
|
||||||
"ok": "Ok",
|
"ok": "Ok",
|
||||||
"yes": "Yes",
|
"yes": "Áno",
|
||||||
"no": "No",
|
"no": "Nie",
|
||||||
"sort_by": "Zoradiť podľa",
|
"sort_by": "Zoradiť podľa",
|
||||||
"ascending": "Vzostupne",
|
"ascending": "Vzostupne",
|
||||||
"descending": "Zostupne",
|
"descending": "Zostupne",
|
||||||
@ -39,7 +39,7 @@
|
|||||||
"body": "Telo textu",
|
"body": "Telo textu",
|
||||||
"message": "Správa",
|
"message": "Správa",
|
||||||
"send": "Odoslať",
|
"send": "Odoslať",
|
||||||
"preview": "Preview",
|
"preview": "Náhľad",
|
||||||
"go_back": "Späť",
|
"go_back": "Späť",
|
||||||
"back_to_login": "Späť na prihlásenie?",
|
"back_to_login": "Späť na prihlásenie?",
|
||||||
"home": "Domov",
|
"home": "Domov",
|
||||||
@ -65,7 +65,7 @@
|
|||||||
"sent": "Odoslané",
|
"sent": "Odoslané",
|
||||||
"all": "Všetko",
|
"all": "Všetko",
|
||||||
"select_all": "Vybrať všetky",
|
"select_all": "Vybrať všetky",
|
||||||
"select_template": "Select Template",
|
"select_template": "Vyberte šablónu",
|
||||||
"choose_file": "Kliknite sem pre vybratie súboru",
|
"choose_file": "Kliknite sem pre vybratie súboru",
|
||||||
"choose_template": "Vybrať vzhľad",
|
"choose_template": "Vybrať vzhľad",
|
||||||
"choose": "Vybrať",
|
"choose": "Vybrať",
|
||||||
@ -92,15 +92,15 @@
|
|||||||
"choose_note": "Vyberte poznámku",
|
"choose_note": "Vyberte poznámku",
|
||||||
"no_note_found": "Neboli nájdené žiadne poznámky",
|
"no_note_found": "Neboli nájdené žiadne poznámky",
|
||||||
"insert_note": "Vlož poznámku",
|
"insert_note": "Vlož poznámku",
|
||||||
"copied_pdf_url_clipboard": "Copied PDF url to clipboard!",
|
"copied_pdf_url_clipboard": "PDF bolo skopírované do schránky!",
|
||||||
"copied_url_clipboard": "Copied url to clipboard!",
|
"copied_url_clipboard": "URL adresa bola skopírovaná do schránky!",
|
||||||
"docs": "Docs",
|
"docs": "Docs",
|
||||||
"do_you_wish_to_continue": "Do you wish to continue?",
|
"do_you_wish_to_continue": "Želáte si pokračovať?",
|
||||||
"note": "Note",
|
"note": "Poznámka",
|
||||||
"pay_invoice": "Pay Invoice",
|
"pay_invoice": "Zaplatiť faktúru",
|
||||||
"login_successfully": "Logged in successfully!",
|
"login_successfully": "Prihlásenie bolo úspešné!",
|
||||||
"logged_out_successfully": "Logged out successfully",
|
"logged_out_successfully": "Odhlásenie bolo úspešné",
|
||||||
"mark_as_default": "Mark as default"
|
"mark_as_default": "Označiť ako predvolené"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"select_year": "Vyberte rok",
|
"select_year": "Vyberte rok",
|
||||||
@ -109,7 +109,7 @@
|
|||||||
"customers": "Zákazníci",
|
"customers": "Zákazníci",
|
||||||
"invoices": "Faktúry",
|
"invoices": "Faktúry",
|
||||||
"estimates": "Cenové odhady",
|
"estimates": "Cenové odhady",
|
||||||
"payments": "Payments"
|
"payments": "Platby"
|
||||||
},
|
},
|
||||||
"chart_info": {
|
"chart_info": {
|
||||||
"total_sales": "Predaje",
|
"total_sales": "Predaje",
|
||||||
@ -151,27 +151,27 @@
|
|||||||
"no_results_found": "Neboli nájdené žiadne výsledky"
|
"no_results_found": "Neboli nájdené žiadne výsledky"
|
||||||
},
|
},
|
||||||
"company_switcher": {
|
"company_switcher": {
|
||||||
"label": "SWITCH COMPANY",
|
"label": "PREPNÚŤ SPOLOČNOSŤ",
|
||||||
"no_results_found": "No Results Found",
|
"no_results_found": "Neboli nájdené žiadne výsledky",
|
||||||
"add_new_company": "Add new company",
|
"add_new_company": "Pridať novú spoločnosť",
|
||||||
"new_company": "New company",
|
"new_company": "Vytvorte spoločnosť",
|
||||||
"created_message": "Company created successfully"
|
"created_message": "Spoločnosť úspešne vytvorená"
|
||||||
},
|
},
|
||||||
"dateRange": {
|
"dateRange": {
|
||||||
"today": "Today",
|
"today": "Dnes",
|
||||||
"this_week": "This Week",
|
"this_week": "Tento týždeň",
|
||||||
"this_month": "This Month",
|
"this_month": "Tento mesiac",
|
||||||
"this_quarter": "This Quarter",
|
"this_quarter": "Tento štvrťrok",
|
||||||
"this_year": "This Year",
|
"this_year": "Tento rok",
|
||||||
"previous_week": "Previous Week",
|
"previous_week": "Predchádzajúci týždeň",
|
||||||
"previous_month": "Previous Month",
|
"previous_month": "Predchádzajúci mesiac",
|
||||||
"previous_quarter": "Previous Quarter",
|
"previous_quarter": "Predchádzajúci štvrťrok",
|
||||||
"previous_year": "Previous Year",
|
"previous_year": "Predchádzajúci rok",
|
||||||
"custom": "Custom"
|
"custom": "Vlastný"
|
||||||
},
|
},
|
||||||
"customers": {
|
"customers": {
|
||||||
"title": "Zákazníci",
|
"title": "Zákazníci",
|
||||||
"prefix": "Prefix",
|
"prefix": "Predpona",
|
||||||
"add_customer": "Pridať Zákazníka",
|
"add_customer": "Pridať Zákazníka",
|
||||||
"contacts_list": "Zoznam zákazníkov",
|
"contacts_list": "Zoznam zákazníkov",
|
||||||
"name": "Meno",
|
"name": "Meno",
|
||||||
@ -186,9 +186,9 @@
|
|||||||
"phone": "Telefón",
|
"phone": "Telefón",
|
||||||
"website": "Webové stránky",
|
"website": "Webové stránky",
|
||||||
"overview": "Prehľad",
|
"overview": "Prehľad",
|
||||||
"invoice_prefix": "Invoice Prefix",
|
"invoice_prefix": "Predpona Faktúry",
|
||||||
"estimate_prefix": "Estimate Prefix",
|
"estimate_prefix": "Predpona cenového odhadu",
|
||||||
"payment_prefix": "Payment Prefix",
|
"payment_prefix": "Predpona platby",
|
||||||
"enable_portal": "Aktivovať portál",
|
"enable_portal": "Aktivovať portál",
|
||||||
"country": "Krajina",
|
"country": "Krajina",
|
||||||
"state": "Štát",
|
"state": "Štát",
|
||||||
@ -197,7 +197,7 @@
|
|||||||
"added_on": "Pridané Dňa",
|
"added_on": "Pridané Dňa",
|
||||||
"action": "Akcia",
|
"action": "Akcia",
|
||||||
"password": "Heslo",
|
"password": "Heslo",
|
||||||
"confirm_password": "Confirm Password",
|
"confirm_password": "Potvrdiť heslo",
|
||||||
"street_number": "Číslo Ulice",
|
"street_number": "Číslo Ulice",
|
||||||
"primary_currency": "Hlavná Mena",
|
"primary_currency": "Hlavná Mena",
|
||||||
"description": "Popis",
|
"description": "Popis",
|
||||||
@ -208,17 +208,17 @@
|
|||||||
"new_customer": "Nový Zákazník",
|
"new_customer": "Nový Zákazník",
|
||||||
"edit_customer": "Upraviť Zákazníka",
|
"edit_customer": "Upraviť Zákazníka",
|
||||||
"basic_info": "Základné Informácie",
|
"basic_info": "Základné Informácie",
|
||||||
"portal_access": "Portal Access",
|
"portal_access": "Prístupy na portál",
|
||||||
"portal_access_text": "Would you like to allow this customer to login to the Customer Portal?",
|
"portal_access_text": "Chcete povoliť tomuto používateľovi pripojiť sa na Zákaznícky Portál?",
|
||||||
"portal_access_url": "Customer Portal Login URL",
|
"portal_access_url": "Adresa zákazníckeho portálu",
|
||||||
"portal_access_url_help": "Please copy & forward the above given URL to your customer for providing access.",
|
"portal_access_url_help": "Prosím, skopírujte a prepošlite URL adresu uvedenú vyššie vášmu klientovi pre udelenie prístupu.",
|
||||||
"billing_address": "Fakturačná Adresa",
|
"billing_address": "Fakturačná Adresa",
|
||||||
"shipping_address": "Doručovacia Adresa",
|
"shipping_address": "Doručovacia Adresa",
|
||||||
"copy_billing_address": "Kopírovať podľa Fakturačnej adresy",
|
"copy_billing_address": "Kopírovať podľa Fakturačnej adresy",
|
||||||
"no_customers": "Zatiaľ nebol pridaný žiadny zákazník!",
|
"no_customers": "Zatiaľ nebol pridaný žiadny zákazník!",
|
||||||
"no_customers_found": "Nenájdení žiadni zákazníci!",
|
"no_customers_found": "Nenájdení žiadni zákazníci!",
|
||||||
"no_contact": "No contact",
|
"no_contact": "Žiadny kontakt",
|
||||||
"no_contact_name": "No contact name",
|
"no_contact_name": "Žiadny kontakt",
|
||||||
"list_of_customers": "Táto sekcia bude obsahovať zoznam zákazníkov.",
|
"list_of_customers": "Táto sekcia bude obsahovať zoznam zákazníkov.",
|
||||||
"primary_display_name": "Hlavné meno pre zobrazenie",
|
"primary_display_name": "Hlavné meno pre zobrazenie",
|
||||||
"select_currency": "Vyberte menu",
|
"select_currency": "Vyberte menu",
|
||||||
@ -231,7 +231,7 @@
|
|||||||
"confirm_delete": "Nebudete môcť obnoviť tohto zákazníka ani žiadne faktúry, cenové odhady alebo platby s ním spojené. | Nebudete môcť obnoviť týchto zákazníkov ani žiadne faktúry, cenové odhady alebo platby s nimi spojené.",
|
"confirm_delete": "Nebudete môcť obnoviť tohto zákazníka ani žiadne faktúry, cenové odhady alebo platby s ním spojené. | Nebudete môcť obnoviť týchto zákazníkov ani žiadne faktúry, cenové odhady alebo platby s nimi spojené.",
|
||||||
"created_message": "Zákazník úspešne vytvorený",
|
"created_message": "Zákazník úspešne vytvorený",
|
||||||
"updated_message": "Zákazník úspešne aktualizovaný",
|
"updated_message": "Zákazník úspešne aktualizovaný",
|
||||||
"address_updated_message": "Address Information Updated succesfully",
|
"address_updated_message": "Nastavenia adresy úspešne aktualizované",
|
||||||
"deleted_message": "Zákazník úspešne odstránený | Zákazníci úspešne odstránení",
|
"deleted_message": "Zákazník úspešne odstránený | Zákazníci úspešne odstránení",
|
||||||
"edit_currency_not_allowed": "Cannot change currency once transactions created."
|
"edit_currency_not_allowed": "Cannot change currency once transactions created."
|
||||||
},
|
},
|
||||||
@ -244,7 +244,7 @@
|
|||||||
"added_on": "Pridané Dňa",
|
"added_on": "Pridané Dňa",
|
||||||
"price": "Cena",
|
"price": "Cena",
|
||||||
"date_of_creation": "Dátum Vytvorenia",
|
"date_of_creation": "Dátum Vytvorenia",
|
||||||
"not_selected": "No item selected",
|
"not_selected": "Nie je vybratá žiadna položka",
|
||||||
"action": "Akcia",
|
"action": "Akcia",
|
||||||
"add_item": "Pridať Položku",
|
"add_item": "Pridať Položku",
|
||||||
"save_item": "Uložiť Položku",
|
"save_item": "Uložiť Položku",
|
||||||
@ -317,7 +317,7 @@
|
|||||||
"required": "Pole je povinné"
|
"required": "Pole je povinné"
|
||||||
},
|
},
|
||||||
"accepted": "Prijátá",
|
"accepted": "Prijátá",
|
||||||
"rejected": "Rejected",
|
"rejected": "Odmietnuté",
|
||||||
"expired": "Expired",
|
"expired": "Expired",
|
||||||
"sent": "Odoslaná",
|
"sent": "Odoslaná",
|
||||||
"draft": "Koncept",
|
"draft": "Koncept",
|
||||||
@ -359,8 +359,8 @@
|
|||||||
},
|
},
|
||||||
"invoices": {
|
"invoices": {
|
||||||
"title": "Faktúry",
|
"title": "Faktúry",
|
||||||
"download": "Download",
|
"download": "Stiahnuť",
|
||||||
"pay_invoice": "Pay Invoice",
|
"pay_invoice": "Zaplatiť faktúru",
|
||||||
"invoices_list": "Zoznam Faktúr",
|
"invoices_list": "Zoznam Faktúr",
|
||||||
"invoice_information": "Invoice Information",
|
"invoice_information": "Invoice Information",
|
||||||
"days": "{days} Ďeň",
|
"days": "{days} Ďeň",
|
||||||
@ -370,8 +370,8 @@
|
|||||||
"paid": "Zaplatené",
|
"paid": "Zaplatené",
|
||||||
"unpaid": "Nezaplatené",
|
"unpaid": "Nezaplatené",
|
||||||
"viewed": "Viewed",
|
"viewed": "Viewed",
|
||||||
"overdue": "Overdue",
|
"overdue": "Po splatnosti",
|
||||||
"completed": "Completed",
|
"completed": "Dokončené",
|
||||||
"customer": "ZÁKAZNÍK",
|
"customer": "ZÁKAZNÍK",
|
||||||
"paid_status": "Stav platby",
|
"paid_status": "Stav platby",
|
||||||
"ref_no": "REF Č.",
|
"ref_no": "REF Č.",
|
||||||
@ -408,7 +408,7 @@
|
|||||||
"invoice_date": "Dátum Vystavenia",
|
"invoice_date": "Dátum Vystavenia",
|
||||||
"record_payment": "Zaznamenať Platbu",
|
"record_payment": "Zaznamenať Platbu",
|
||||||
"add_new_invoice": "Nová Faktúra",
|
"add_new_invoice": "Nová Faktúra",
|
||||||
"update_expense": "Update Expense",
|
"update_expense": "Aktualizovať Výdaj",
|
||||||
"edit_invoice": "Upraviť Faktúru",
|
"edit_invoice": "Upraviť Faktúru",
|
||||||
"new_invoice": "Nová Faktúra",
|
"new_invoice": "Nová Faktúra",
|
||||||
"save_invoice": "Uložiť Faktúru",
|
"save_invoice": "Uložiť Faktúru",
|
||||||
@ -421,7 +421,7 @@
|
|||||||
"select_invoice": "Vybrať Faktúru",
|
"select_invoice": "Vybrať Faktúru",
|
||||||
"no_matching_invoices": "Nenašli sa žiadne faktúry!",
|
"no_matching_invoices": "Nenašli sa žiadne faktúry!",
|
||||||
"mark_as_sent_successfully": "Faktúra označená ako úspešne odoslaná",
|
"mark_as_sent_successfully": "Faktúra označená ako úspešne odoslaná",
|
||||||
"invoice_sent_successfully": "Invoice sent successfully",
|
"invoice_sent_successfully": "Faktúra bola úspešne odoslaná",
|
||||||
"cloned_successfully": "Faktúra bola úspešne okopírovaná",
|
"cloned_successfully": "Faktúra bola úspešne okopírovaná",
|
||||||
"clone_invoice": "Kopírovať faktúru",
|
"clone_invoice": "Kopírovať faktúru",
|
||||||
"confirm_clone": "Faktúra bude okopírovaná do novej",
|
"confirm_clone": "Faktúra bude okopírovaná do novej",
|
||||||
@ -450,34 +450,34 @@
|
|||||||
"mark_as_default_invoice_template_description": "If enabled, the selected template will be automatically selected for new invoices."
|
"mark_as_default_invoice_template_description": "If enabled, the selected template will be automatically selected for new invoices."
|
||||||
},
|
},
|
||||||
"recurring_invoices": {
|
"recurring_invoices": {
|
||||||
"title": "Recurring Invoices",
|
"title": "Pravidelné faktúry",
|
||||||
"invoices_list": "Recurring Invoices List",
|
"invoices_list": "Zoznam pravidelných faktúr",
|
||||||
"days": "{days} Days",
|
"days": "{days} Days",
|
||||||
"months": "{months} Month",
|
"months": "{months} Month",
|
||||||
"years": "{years} Year",
|
"years": "{years} Year",
|
||||||
"all": "All",
|
"all": "Všetko",
|
||||||
"paid": "Paid",
|
"paid": "Zaplatené",
|
||||||
"unpaid": "Unpaid",
|
"unpaid": "Nezaplatené",
|
||||||
"viewed": "Viewed",
|
"viewed": "Viewed",
|
||||||
"overdue": "Overdue",
|
"overdue": "Po splatnosti",
|
||||||
"active": "Active",
|
"active": "Active",
|
||||||
"completed": "Completed",
|
"completed": "Dokončené",
|
||||||
"customer": "CUSTOMER",
|
"customer": "ZÁKAZNÍK",
|
||||||
"paid_status": "PAID STATUS",
|
"paid_status": "PAID STATUS",
|
||||||
"ref_no": "REF NO.",
|
"ref_no": "REF Č.",
|
||||||
"number": "NUMBER",
|
"number": "ČÍSLO",
|
||||||
"amount_due": "AMOUNT DUE",
|
"amount_due": "AMOUNT DUE",
|
||||||
"partially_paid": "Partially Paid",
|
"partially_paid": "Čiastočne Zaplatené",
|
||||||
"total": "Total",
|
"total": "Celkom",
|
||||||
"discount": "Discount",
|
"discount": "Zľava",
|
||||||
"sub_total": "Sub Total",
|
"sub_total": "Medzisúčet",
|
||||||
"invoice": "Recurring Invoice | Recurring Invoices",
|
"invoice": "Pravidelná faktúra | Pravidelné faktúry",
|
||||||
"invoice_number": "Recurring Invoice Number",
|
"invoice_number": "Číslo pravidelnej faktúry",
|
||||||
"next_invoice_date": "Next Invoice Date",
|
"next_invoice_date": "Dátum nasledujúceho vystavenia",
|
||||||
"ref_number": "Ref Number",
|
"ref_number": "Ref. Číslo",
|
||||||
"contact": "Contact",
|
"contact": "Kontakt",
|
||||||
"add_item": "Add an Item",
|
"add_item": "Pridať položku",
|
||||||
"date": "Date",
|
"date": "Dátum",
|
||||||
"limit_by": "Limit by",
|
"limit_by": "Limit by",
|
||||||
"limit_date": "Limit Date",
|
"limit_date": "Limit Date",
|
||||||
"limit_count": "Limit Count",
|
"limit_count": "Limit Count",
|
||||||
@ -506,15 +506,15 @@
|
|||||||
"starts_at": "Start Date",
|
"starts_at": "Start Date",
|
||||||
"due_date": "Invoice Due Date",
|
"due_date": "Invoice Due Date",
|
||||||
"record_payment": "Record Payment",
|
"record_payment": "Record Payment",
|
||||||
"add_new_invoice": "Add New Recurring Invoice",
|
"add_new_invoice": "Vytvoriť novú pravidelnú faktúru",
|
||||||
"update_expense": "Update Expense",
|
"update_expense": "Aktualizovať Výdaj",
|
||||||
"edit_invoice": "Edit Recurring Invoice",
|
"edit_invoice": "Upraviť pravidelnú faktúru",
|
||||||
"new_invoice": "New Recurring Invoice",
|
"new_invoice": "Nová pravidelná faktúra",
|
||||||
"send_automatically": "Send Automatically",
|
"send_automatically": "Odoslať automaticky",
|
||||||
"send_automatically_desc": "Enable this, if you would like to send the invoice automatically to the customer when its created.",
|
"send_automatically_desc": "Enable this, if you would like to send the invoice automatically to the customer when its created.",
|
||||||
"save_invoice": "Save Recurring Invoice",
|
"save_invoice": "Uložiť pravidelnú faktúru",
|
||||||
"update_invoice": "Update Recurring Invoice",
|
"update_invoice": "Upraviť pravidelnú faktúru",
|
||||||
"add_new_tax": "Add New Tax",
|
"add_new_tax": "Pridať novú daň",
|
||||||
"no_invoices": "No Recurring Invoices yet!",
|
"no_invoices": "No Recurring Invoices yet!",
|
||||||
"mark_as_rejected": "Mark as rejected",
|
"mark_as_rejected": "Mark as rejected",
|
||||||
"mark_as_accepted": "Mark as accepted",
|
"mark_as_accepted": "Mark as accepted",
|
||||||
@ -528,27 +528,27 @@
|
|||||||
"confirm_clone": "This recurring invoice will be cloned into a new Recurring Invoice",
|
"confirm_clone": "This recurring invoice will be cloned into a new Recurring Invoice",
|
||||||
"add_customer_email": "Please add an email address for this customer to send invoices automatically.",
|
"add_customer_email": "Please add an email address for this customer to send invoices automatically.",
|
||||||
"item": {
|
"item": {
|
||||||
"title": "Item Title",
|
"title": "Názov položky",
|
||||||
"description": "Description",
|
"description": "Popis",
|
||||||
"quantity": "Quantity",
|
"quantity": "Množstvo",
|
||||||
"price": "Price",
|
"price": "Cena",
|
||||||
"discount": "Discount",
|
"discount": "Zľava",
|
||||||
"total": "Total",
|
"total": "Súčet",
|
||||||
"total_discount": "Total Discount",
|
"total_discount": "Celková zľava",
|
||||||
"sub_total": "Sub Total",
|
"sub_total": "Medzisúčet",
|
||||||
"tax": "Tax",
|
"tax": "Daň",
|
||||||
"amount": "Amount",
|
"amount": "Množstvo",
|
||||||
"select_an_item": "Type or click to select an item",
|
"select_an_item": "Začnite písať alebo kliknite pre vybratie položky",
|
||||||
"type_item_description": "Type Item Description (optional)"
|
"type_item_description": "Popis položky (voliteľné)"
|
||||||
},
|
},
|
||||||
"frequency": {
|
"frequency": {
|
||||||
"title": "Frequency",
|
"title": "Frekvencia",
|
||||||
"select_frequency": "Select Frequency",
|
"select_frequency": "Vybrať frekvenciu",
|
||||||
"minute": "Minute",
|
"minute": "Minúta",
|
||||||
"hour": "Hour",
|
"hour": "Hodina",
|
||||||
"day_month": "Day of month",
|
"day_month": "Deň v mesiaci",
|
||||||
"month": "Month",
|
"month": "Mesiac",
|
||||||
"day_week": "Day of week"
|
"day_week": "Deň v týždni"
|
||||||
},
|
},
|
||||||
"confirm_delete": "You will not be able to recover this Invoice | You will not be able to recover these Invoices",
|
"confirm_delete": "You will not be able to recover this Invoice | You will not be able to recover these Invoices",
|
||||||
"created_message": "Recurring Invoice created successfully",
|
"created_message": "Recurring Invoice created successfully",
|
||||||
@ -686,12 +686,12 @@
|
|||||||
"api_token": "API token",
|
"api_token": "API token",
|
||||||
"invalid_api_token": "Invalid API Token.",
|
"invalid_api_token": "Invalid API Token.",
|
||||||
"other_modules": "Other Modules",
|
"other_modules": "Other Modules",
|
||||||
"view_all": "View All",
|
"view_all": "Zobraziť všetky",
|
||||||
"no_reviews_found": "There are no reviews for this module yet!",
|
"no_reviews_found": "Zatiaľ nie sú dostupné žiadne hodnotenia pre tento modul!",
|
||||||
"module_not_purchased": "Module Not Purchased",
|
"module_not_purchased": "Modul nie je kúpený",
|
||||||
"module_not_found": "Module Not Found",
|
"module_not_found": "Modul nebol nájdený",
|
||||||
"version_not_supported": "This module version doesn't support the current version of Crater",
|
"version_not_supported": "This module version doesn't support the current version of Crater",
|
||||||
"last_updated": "Last Updated On",
|
"last_updated": "Naposledy aktualizované",
|
||||||
"connect_installation": "Connect your installation",
|
"connect_installation": "Connect your installation",
|
||||||
"api_token_description": "Login to {url} and connect this installation by entering the API Token. Your purchased modules will show up here after the connection is established.",
|
"api_token_description": "Login to {url} and connect this installation by entering the API Token. Your purchased modules will show up here after the connection is established.",
|
||||||
"view_module": "View Module",
|
"view_module": "View Module",
|
||||||
@ -806,8 +806,8 @@
|
|||||||
"custom_fields": "Vlastné Polia",
|
"custom_fields": "Vlastné Polia",
|
||||||
"payment_modes": "Spôsoby Platby",
|
"payment_modes": "Spôsoby Platby",
|
||||||
"notes": "Poznámky",
|
"notes": "Poznámky",
|
||||||
"exchange_rate": "Exchange Rate",
|
"exchange_rate": "Výmenný kurz",
|
||||||
"address_information": "Address Information"
|
"address_information": "Údaje adresy"
|
||||||
},
|
},
|
||||||
"address_information": {
|
"address_information": {
|
||||||
"section_description": " You can update Your Address information using form below."
|
"section_description": " You can update Your Address information using form below."
|
||||||
@ -872,9 +872,9 @@
|
|||||||
"address": "Adresa",
|
"address": "Adresa",
|
||||||
"zip": "PSČ",
|
"zip": "PSČ",
|
||||||
"save": "Uložiť",
|
"save": "Uložiť",
|
||||||
"delete": "Delete",
|
"delete": "Odstrániť",
|
||||||
"updated_message": "Informácie o firme úspešne aktualizované",
|
"updated_message": "Informácie o firme úspešne aktualizované",
|
||||||
"delete_company": "Delete Company",
|
"delete_company": "Odstrániť spoločnosť",
|
||||||
"delete_company_description": "Once you delete your company, you will lose all the data and files associated with it permanently.",
|
"delete_company_description": "Once you delete your company, you will lose all the data and files associated with it permanently.",
|
||||||
"are_you_absolutely_sure": "Are you absolutely sure?",
|
"are_you_absolutely_sure": "Are you absolutely sure?",
|
||||||
"delete_company_modal_desc": "This action cannot be undone. This will permanently delete {company} and all of its associated data.",
|
"delete_company_modal_desc": "This action cannot be undone. This will permanently delete {company} and all of its associated data.",
|
||||||
@ -1485,7 +1485,7 @@
|
|||||||
"pdf_estimate_label": "Cenový odhad",
|
"pdf_estimate_label": "Cenový odhad",
|
||||||
"pdf_estimate_number": "Číslo cenového odhadu",
|
"pdf_estimate_number": "Číslo cenového odhadu",
|
||||||
"pdf_estimate_date": "Dátum cenového odhadu",
|
"pdf_estimate_date": "Dátum cenového odhadu",
|
||||||
"pdf_estimate_expire_date": "Platnosť cenového odhadu",
|
"pdf_estimate_expire_date": "Expiry Date",
|
||||||
"pdf_invoice_label": "Faktúra",
|
"pdf_invoice_label": "Faktúra",
|
||||||
"pdf_invoice_number": "Číslo faktúry",
|
"pdf_invoice_number": "Číslo faktúry",
|
||||||
"pdf_invoice_date": "Dátum vystavenia",
|
"pdf_invoice_date": "Dátum vystavenia",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user