mirror of
https://github.com/crater-invoice/crater.git
synced 2025-10-28 04:01:10 -04:00
Compare commits
31 Commits
dennisreim
...
mail-sende
| Author | SHA1 | Date | |
|---|---|---|---|
| dea73bcdf8 | |||
| aececb8575 | |||
| 2bea727d19 | |||
| aede1f76d0 | |||
| 959aa257b4 | |||
| b4aa254b68 | |||
| c1f2af5174 | |||
| 05d5ce26fd | |||
| 393fe20010 | |||
| 57bdbd2897 | |||
| 7447cc24f9 | |||
| 889d22d92c | |||
| bc8f2cd484 | |||
| 4e47f58bad | |||
| d8c429912e | |||
| 0aaf0e7e75 | |||
| 3d0b89bb4d | |||
| 38c4b9ebce | |||
| 7be59e78e0 | |||
| 204483836a | |||
| 33bc9ded65 | |||
| 4271ef451e | |||
| 6eb44fba93 | |||
| 96e7300583 | |||
| a479d966d1 | |||
| bca2794c4c | |||
| cb88c19059 | |||
| 946c7efab4 | |||
| d7b1d15f93 | |||
| 94e1efe115 | |||
| b0e38b74e9 |
161
.github/workflows/uffizzi-build.yml
vendored
Normal file
161
.github/workflows/uffizzi-build.yml
vendored
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
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
Normal file
84
.github/workflows/uffizzi-preview.yml
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
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,3 +16,5 @@ Homestead.yaml
|
|||||||
.gitkeep
|
.gitkeep
|
||||||
/public/docs
|
/public/docs
|
||||||
/.scribe
|
/.scribe
|
||||||
|
!storage/fonts/.gitkeep
|
||||||
|
.DS_Store
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
FROM php:7.4-fpm
|
FROM php:8.1-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("app/pdf/{$type}/{$templateName}.blade.php");
|
$path = resource_path("views/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);
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,24 @@
|
|||||||
|
<?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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,98 @@
|
|||||||
|
<?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,24 +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\Currency;
|
||||||
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 PDF;
|
use Crater\Http\Controllers\Controller;
|
||||||
|
|
||||||
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();
|
||||||
@ -56,6 +57,7 @@ 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',
|
||||||
@ -80,6 +82,7 @@ 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,24 +2,25 @@
|
|||||||
|
|
||||||
namespace Crater\Http\Controllers\V1\Admin\Report;
|
namespace Crater\Http\Controllers\V1\Admin\Report;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
|
||||||
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;
|
use PDF;
|
||||||
|
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;
|
||||||
|
|
||||||
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();
|
||||||
@ -43,6 +44,7 @@ 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',
|
||||||
@ -66,6 +68,7 @@ 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,24 +2,25 @@
|
|||||||
|
|
||||||
namespace Crater\Http\Controllers\V1\Admin\Report;
|
namespace Crater\Http\Controllers\V1\Admin\Report;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
|
||||||
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;
|
use PDF;
|
||||||
|
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;
|
||||||
|
|
||||||
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();
|
||||||
@ -43,6 +44,7 @@ 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',
|
||||||
@ -66,6 +68,7 @@ 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,25 +2,26 @@
|
|||||||
|
|
||||||
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 PDF;
|
use Crater\Http\Controllers\Controller;
|
||||||
|
|
||||||
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();
|
||||||
@ -49,6 +50,8 @@ 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',
|
||||||
@ -74,6 +77,7 @@ 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,24 +2,25 @@
|
|||||||
|
|
||||||
namespace Crater\Http\Controllers\V1\Admin\Report;
|
namespace Crater\Http\Controllers\V1\Admin\Report;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
|
||||||
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;
|
use PDF;
|
||||||
|
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;
|
||||||
|
|
||||||
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();
|
||||||
@ -44,6 +45,8 @@ 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',
|
||||||
@ -68,6 +71,7 @@ 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,80 +3,29 @@
|
|||||||
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\MailEnvironmentRequest;
|
use Crater\Http\Requests\TestMailDriverRequest;
|
||||||
use Crater\Mail\TestMail;
|
use Crater\Mail\TestMail;
|
||||||
use Crater\Models\Setting;
|
use Crater\Models\MailSender;
|
||||||
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');
|
||||||
|
|
||||||
$setting = Setting::getSetting('profile_complete');
|
MailSender::setMailConfiguration($request->mail_sender_id);
|
||||||
$results = $this->environmentManager->saveMailVariables($request);
|
|
||||||
|
|
||||||
if ($setting !== 'COMPLETED') {
|
Mail::to($request->to)->send(new TestMail($request->subject, $request->message));
|
||||||
Setting::setSetting('profile_complete', 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
return response()->json($results);
|
return response()->json([
|
||||||
|
'success' => true,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMailEnvironment()
|
public function getMailDrivers(Request $request)
|
||||||
{
|
{
|
||||||
$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',
|
||||||
@ -87,21 +36,4 @@ 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,6 +9,7 @@ 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
|
||||||
@ -27,14 +28,16 @@ class EstimatePdfController extends Controller
|
|||||||
);
|
);
|
||||||
|
|
||||||
if ($notifyEstimateViewed == 'YES') {
|
if ($notifyEstimateViewed == 'YES') {
|
||||||
$data['estimate'] = Estimate::findOrFail($estimate->id)->toArray();
|
$notificationEmail = CompanySetting::getSetting('notification_email', $estimate->company_id);
|
||||||
$data['user'] = Customer::find($estimate->customer_id)->toArray();
|
$mailSender = MailSender::where('company_id', $estimate->company_id)->where('is_default', true)->first();
|
||||||
$notificationEmail = CompanySetting::getSetting(
|
MailSender::setMailConfiguration($mailSender->id);
|
||||||
'notification_email',
|
|
||||||
$estimate->company_id
|
|
||||||
);
|
|
||||||
|
|
||||||
\Mail::to($notificationEmail)->send(new EstimateViewedMail($data));
|
$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();
|
||||||
|
|
||||||
|
send_mail(new EstimateViewedMail($data), $mailSender, $notificationEmail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ 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
|
||||||
@ -28,14 +29,16 @@ 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
|
|
||||||
);
|
|
||||||
|
|
||||||
\Mail::to($notificationEmail)->send(new InvoiceViewedMail($data));
|
send_mail(new InvoiceViewedMail($data), $mailSender, $notificationEmail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@ namespace Crater\Http\Middleware;
|
|||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
use Crater\Models\FileDisk;
|
use Crater\Models\FileDisk;
|
||||||
|
use Crater\Models\MailSender;
|
||||||
|
|
||||||
class ConfigMiddleware
|
class ConfigMiddleware
|
||||||
{
|
{
|
||||||
@ -28,6 +29,12 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
85
app/Http/Requests/MailSenderRequest.php
Normal file
85
app/Http/Requests/MailSenderRequest.php
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<?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',
|
||||||
],
|
],
|
||||||
'from' => [
|
'mail_sender_id' => [
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'to' => [
|
'to' => [
|
||||||
|
|||||||
@ -30,7 +30,7 @@ class SendInvoiceRequest extends FormRequest
|
|||||||
'subject' => [
|
'subject' => [
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'from' => [
|
'mail_sender_id' => [
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'to' => [
|
'to' => [
|
||||||
|
|||||||
@ -30,7 +30,7 @@ class SendPaymentRequest extends FormRequest
|
|||||||
'body' => [
|
'body' => [
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'from' => [
|
'mail_sender_id' => [
|
||||||
'required',
|
'required',
|
||||||
],
|
],
|
||||||
'to' => [
|
'to' => [
|
||||||
|
|||||||
39
app/Http/Requests/TestMailDriverRequest.php
Normal file
39
app/Http/Requests/TestMailDriverRequest.php
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?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'
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
30
app/Http/Resources/MailSenderResource.php
Normal file
30
app/Http/Resources/MailSenderResource.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?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(config('mail.from.address'), config('mail.from.name'))
|
return $this->from($this->data['from_address'], $this->data['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(config('mail.from.address'), config('mail.from.name'))
|
return $this->from($this->data['from_address'], $this->data['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'],
|
'from' => $this->data['from_address'],
|
||||||
'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,10 @@ 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'], config('mail.from.name'))
|
$mailContent = $this->from($this->data['from_address'], $this->data['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'],
|
'from' => $this->data['from_address'],
|
||||||
'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'], config('mail.from.name'))
|
$mailContent = $this->from($this->data['from_address'], $this->data['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'],
|
'from' => $this->data['from_address'],
|
||||||
'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'], config('mail.from.name'))
|
$mailContent = $this->from($this->data['from_address'], $this->data['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,6 +20,7 @@ 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;
|
||||||
@ -363,7 +364,7 @@ class Estimate extends Model implements HasMedia
|
|||||||
$this->save();
|
$this->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
\Mail::to($data['to'])->send(new SendEstimateMail($data));
|
$this->setMail('estimate', $data);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'success' => true,
|
'success' => true,
|
||||||
|
|||||||
@ -9,6 +9,7 @@ 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;
|
||||||
@ -21,6 +22,7 @@ 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;
|
||||||
@ -443,7 +445,8 @@ class Invoice extends Model implements HasMedia
|
|||||||
$data['invoice'] = $this->toArray();
|
$data['invoice'] = $this->toArray();
|
||||||
$data['customer'] = $this->customer->toArray();
|
$data['customer'] = $this->customer->toArray();
|
||||||
$data['company'] = Company::find($this->company_id);
|
$data['company'] = Company::find($this->company_id);
|
||||||
$data['body'] = $this->getEmailBody($data['body']);
|
$data['subject'] = $this->getEmailString($data['subject']);
|
||||||
|
$data['body'] = $this->getEmailString($data['body']);
|
||||||
$data['attach']['data'] = ($this->getEmailAttachmentSetting()) ? $this->getPDFData() : null;
|
$data['attach']['data'] = ($this->getEmailAttachmentSetting()) ? $this->getPDFData() : null;
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
@ -463,7 +466,7 @@ class Invoice extends Model implements HasMedia
|
|||||||
{
|
{
|
||||||
$data = $this->sendInvoiceData($data);
|
$data = $this->sendInvoiceData($data);
|
||||||
|
|
||||||
\Mail::to($data['to'])->send(new SendInvoiceMail($data));
|
$this->setMail('invoice', $data);
|
||||||
|
|
||||||
if ($this->status == Invoice::STATUS_DRAFT) {
|
if ($this->status == Invoice::STATUS_DRAFT) {
|
||||||
$this->status = Invoice::STATUS_SENT;
|
$this->status = Invoice::STATUS_SENT;
|
||||||
@ -635,7 +638,7 @@ class Invoice extends Model implements HasMedia
|
|||||||
return $this->getFormattedString($this->notes);
|
return $this->getFormattedString($this->notes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getEmailBody($body)
|
public function getEmailString($body)
|
||||||
{
|
{
|
||||||
$values = array_merge($this->getFieldsArray(), $this->getExtraFields());
|
$values = array_merge($this->getFieldsArray(), $this->getExtraFields());
|
||||||
|
|
||||||
|
|||||||
111
app/Models/MailSender.php
Normal file
111
app/Models/MailSender.php
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<?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,6 +18,7 @@ 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;
|
||||||
@ -135,7 +136,7 @@ class Payment extends Model implements HasMedia
|
|||||||
{
|
{
|
||||||
$data = $this->sendPaymentData($data);
|
$data = $this->sendPaymentData($data);
|
||||||
|
|
||||||
\Mail::to($data['to'])->send(new SendPaymentMail($data));
|
$this->setMail('payment', $data);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'success' => true,
|
'success' => true,
|
||||||
|
|||||||
123
app/Policies/MailSenderPolicy.php
Normal file
123
app/Policies/MailSenderPolicy.php
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
<?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,6 +39,7 @@ 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,204 +223,6 @@ 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,6 +5,7 @@ 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
|
||||||
@ -70,6 +71,42 @@ 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
|
||||||
|
|||||||
40
app/Traits/MailTrait.php
Normal file
40
app/Traits/MailTrait.php
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<?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.0",
|
"friendsofphp/php-cs-fixer": "^3.8",
|
||||||
"fzaninotto/faker": "^1.9.1",
|
"fakerphp/faker": "^1.9.1",
|
||||||
"mockery/mockery": "^1.3.1",
|
"mockery/mockery": "^1.3.1",
|
||||||
"nunomaduro/collision": "^5.0",
|
"nunomaduro/collision": "^5.0",
|
||||||
"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.0"
|
"phpunit/phpunit": "^9.3"
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
@ -81,11 +81,14 @@
|
|||||||
"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": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2347
composer.lock
generated
2347
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -7,6 +7,7 @@ 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;
|
||||||
@ -397,6 +398,41 @@ 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,6 +7,7 @@ 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;
|
||||||
@ -71,6 +72,7 @@ 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" => "ไทย"],
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -224,6 +226,17 @@ 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' => '',
|
||||||
@ -234,16 +247,6 @@ 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' => '',
|
||||||
@ -274,6 +277,7 @@ return [
|
|||||||
'ability' => '',
|
'ability' => '',
|
||||||
'model' => ''
|
'model' => ''
|
||||||
],
|
],
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -27,6 +27,7 @@ return [
|
|||||||
'tokenizer',
|
'tokenizer',
|
||||||
'JSON',
|
'JSON',
|
||||||
'cURL',
|
'cURL',
|
||||||
|
'zip',
|
||||||
],
|
],
|
||||||
'apache' => [
|
'apache' => [
|
||||||
'mod_rewrite',
|
'mod_rewrite',
|
||||||
|
|||||||
@ -0,0 +1,106 @@
|
|||||||
|
<?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 The",'phonecode' => 31],
|
['id' => 155,'code' => 'NL','name' => "Netherlands",'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:7.4-fpm-alpine
|
FROM php:8.0-fpm-alpine
|
||||||
|
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
php7-bcmath
|
php8-bcmath
|
||||||
|
|
||||||
RUN docker-php-ext-install pdo pdo_mysql bcmath
|
RUN docker-php-ext-install pdo pdo_mysql bcmath
|
||||||
|
|
||||||
|
|||||||
@ -47,8 +47,6 @@ 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')
|
||||||
@ -56,6 +54,8 @@ 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,13 +302,6 @@ 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',
|
||||||
@ -327,6 +320,13 @@ 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,
|
||||||
|
},
|
||||||
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
123
resources/scripts/admin/components/FeedbackAlert.vue
Normal file
123
resources/scripts/admin/components/FeedbackAlert.vue
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
<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>
|
||||||
@ -0,0 +1,111 @@
|
|||||||
|
<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,18 +17,7 @@
|
|||||||
<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="
|
class="flex items-center justify-center w-5 h-5 mt-2 mr-2 text-gray-300 cursor-move handle"
|
||||||
flex
|
|
||||||
items-center
|
|
||||||
justify-center
|
|
||||||
w-5
|
|
||||||
h-5
|
|
||||||
mt-2
|
|
||||||
text-gray-300
|
|
||||||
cursor-move
|
|
||||||
handle
|
|
||||||
mr-2
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<DragIcon />
|
<DragIcon />
|
||||||
</div>
|
</div>
|
||||||
@ -108,7 +97,7 @@
|
|||||||
|
|
||||||
<BaseIcon
|
<BaseIcon
|
||||||
name="ChevronDownIcon"
|
name="ChevronDownIcon"
|
||||||
class="w-4 h-4 text-gray-500 ml-1"
|
class="w-4 h-4 ml-1 text-gray-500"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
@ -155,7 +144,7 @@
|
|||||||
<BaseContentPlaceholders v-if="loading">
|
<BaseContentPlaceholders v-if="loading">
|
||||||
<BaseContentPlaceholdersText
|
<BaseContentPlaceholdersText
|
||||||
:lines="1"
|
:lines="1"
|
||||||
class="w-24 h-8 rounded-md border"
|
class="w-24 h-8 border rounded-md"
|
||||||
/>
|
/>
|
||||||
</BaseContentPlaceholders>
|
</BaseContentPlaceholders>
|
||||||
|
|
||||||
@ -175,6 +164,7 @@
|
|||||||
: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,24 +30,13 @@
|
|||||||
<template v-if="userStore.hasAbilities(ability)" #action>
|
<template v-if="userStore.hasAbilities(ability)" #action>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="
|
class="flex items-center justify-center w-full px-2 py-2 bg-gray-200 border-none outline-none cursor-pointer "
|
||||||
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 text-primary-400 cursor-pointer"
|
class="ml-2 text-sm leading-none cursor-pointer text-primary-400"
|
||||||
>{{ $t('invoices.add_new_tax') }}</label
|
>{{ $t('invoices.add_new_tax') }}</label
|
||||||
>
|
>
|
||||||
</button>
|
</button>
|
||||||
@ -115,6 +104,10 @@ 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,
|
||||||
@ -153,19 +146,19 @@ const filteredTypes = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const taxAmount = computed(() => {
|
const taxAmount = computed(() => {
|
||||||
if (localTax.compound_tax && props.total) {
|
if (localTax.compound_tax && props.discountedTotal) {
|
||||||
return ((props.total + props.totalTax) * localTax.percent) / 100
|
return ((props.discountedTotal + props.totalTax) * localTax.percent) / 100
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.total && localTax.percent) {
|
if (props.discountedTotal && localTax.percent) {
|
||||||
return (props.total * localTax.percent) / 100
|
return (props.discountedTotal * localTax.percent) / 100
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.total,
|
() => props.discountedTotal,
|
||||||
() => {
|
() => {
|
||||||
updateRowTax()
|
updateRowTax()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,14 +29,7 @@
|
|||||||
|
|
||||||
<label
|
<label
|
||||||
v-else
|
v-else
|
||||||
class="
|
class="flex items-center justify-center m-0 text-lg text-black uppercase "
|
||||||
flex
|
|
||||||
items-center
|
|
||||||
justify-center
|
|
||||||
m-0
|
|
||||||
text-lg text-black
|
|
||||||
uppercase
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<BaseFormatMoney
|
<BaseFormatMoney
|
||||||
:amount="store.getSubTotal"
|
:amount="store.getSubTotal"
|
||||||
@ -66,14 +59,7 @@
|
|||||||
|
|
||||||
<label
|
<label
|
||||||
v-else-if="store[storeProp].tax_per_item === 'YES'"
|
v-else-if="store[storeProp].tax_per_item === 'YES'"
|
||||||
class="
|
class="flex items-center justify-center m-0 text-lg text-black uppercase "
|
||||||
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>
|
||||||
@ -98,7 +84,7 @@
|
|||||||
<BaseContentPlaceholders v-if="isLoading">
|
<BaseContentPlaceholders v-if="isLoading">
|
||||||
<BaseContentPlaceholdersText
|
<BaseContentPlaceholdersText
|
||||||
:lines="1"
|
:lines="1"
|
||||||
class="w-24 h-8 rounded-md border"
|
class="w-24 h-8 border rounded-md"
|
||||||
/>
|
/>
|
||||||
</BaseContentPlaceholders>
|
</BaseContentPlaceholders>
|
||||||
<div v-else class="flex" style="width: 140px" role="group">
|
<div v-else class="flex" style="width: 140px" role="group">
|
||||||
@ -114,7 +100,7 @@
|
|||||||
<BaseDropdown position="bottom-end">
|
<BaseDropdown position="bottom-end">
|
||||||
<template #activator>
|
<template #activator>
|
||||||
<BaseButton
|
<BaseButton
|
||||||
class="rounded-tr-md rounded-br-md p-2 rounded-none"
|
class="p-2 rounded-none rounded-tr-md rounded-br-md"
|
||||||
type="button"
|
type="button"
|
||||||
variant="white"
|
variant="white"
|
||||||
>
|
>
|
||||||
@ -127,7 +113,7 @@
|
|||||||
|
|
||||||
<BaseIcon
|
<BaseIcon
|
||||||
name="ChevronDownIcon"
|
name="ChevronDownIcon"
|
||||||
class="w-4 h-4 text-gray-500 ml-1"
|
class="w-4 h-4 ml-1 text-gray-500"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
</BaseButton>
|
</BaseButton>
|
||||||
@ -180,15 +166,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="
|
class="flex items-center justify-between w-full pt-2 mt-5 border-t border-gray-200 border-solid "
|
||||||
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" />
|
||||||
@ -204,14 +182,7 @@
|
|||||||
</BaseContentPlaceholders>
|
</BaseContentPlaceholders>
|
||||||
<label
|
<label
|
||||||
v-else
|
v-else
|
||||||
class="
|
class="flex items-center justify-center text-lg uppercase text-primary-400"
|
||||||
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>
|
||||||
@ -334,6 +305,7 @@ 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, onMounted, ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
|
||||||
@ -549,7 +549,6 @@ 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(
|
||||||
|
|||||||
@ -0,0 +1,287 @@
|
|||||||
|
<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>
|
||||||
@ -0,0 +1,208 @@
|
|||||||
|
<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,18 +16,28 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<form v-if="!isPreview" action="">
|
<form v-if="!isPreview" action="">
|
||||||
<div class="px-8 py-8 sm:p-6">
|
<!-- v-if -->
|
||||||
|
<div v-if="isMailSenderExist" class="px-8 py-8 sm:p-6">
|
||||||
<BaseInputGrid layout="one-column">
|
<BaseInputGrid layout="one-column">
|
||||||
<BaseInputGroup
|
<BaseInputGroup
|
||||||
:label="$t('general.from')"
|
:label="$tc('settings.mail_sender.title', 1)"
|
||||||
required
|
required
|
||||||
:error="v$.from.$error && v$.from.$errors[0].$message"
|
:error="
|
||||||
|
v$.mail_sender_id.$error && v$.mail_sender_id.$errors[0].$message
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<BaseInput
|
<BaseMultiselect
|
||||||
v-model="estimateMailForm.from"
|
v-model="estimateMailForm.mail_sender_id"
|
||||||
type="text"
|
:invalid="v$.mail_sender_id.$error"
|
||||||
:invalid="v$.from.$error"
|
label="name"
|
||||||
@input="v$.from.$touch()"
|
:options="mailSenders"
|
||||||
|
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
|
||||||
@ -62,6 +72,45 @@
|
|||||||
</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"
|
||||||
>
|
>
|
||||||
@ -75,6 +124,7 @@
|
|||||||
</BaseButton>
|
</BaseButton>
|
||||||
|
|
||||||
<BaseButton
|
<BaseButton
|
||||||
|
v-if="isMailSenderExist"
|
||||||
:loading="isLoading"
|
:loading="isLoading"
|
||||||
:disabled="isLoading"
|
:disabled="isLoading"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
@ -141,18 +191,24 @@ 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 { useMailDriverStore } from '@/scripts/admin/stores/mail-driver'
|
import { useMailSenderStore } from '@/scripts/admin/stores/mail-sender'
|
||||||
|
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 mailDriverStore = useMailDriverStore()
|
const mailSenderStore = useMailSenderStore()
|
||||||
|
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',
|
||||||
@ -164,7 +220,7 @@ const estimateMailFields = ref([
|
|||||||
|
|
||||||
let estimateMailForm = reactive({
|
let estimateMailForm = reactive({
|
||||||
id: null,
|
id: null,
|
||||||
from: null,
|
mail_sender_id: null,
|
||||||
to: null,
|
to: null,
|
||||||
subject: 'New Estimate',
|
subject: 'New Estimate',
|
||||||
body: null,
|
body: null,
|
||||||
@ -181,9 +237,8 @@ const modalData = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
from: {
|
mail_sender_id: {
|
||||||
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),
|
||||||
@ -207,20 +262,26 @@ 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() {
|
||||||
@ -274,4 +335,18 @@ 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,18 +15,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<form v-if="!isPreview" action="">
|
<form v-if="!isPreview" action="">
|
||||||
<div class="px-8 py-8 sm:p-6">
|
<!-- v-if -->
|
||||||
|
<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="$t('general.from')"
|
:label="$tc('settings.mail_sender.title', 1)"
|
||||||
required
|
required
|
||||||
:error="v$.from.$error && v$.from.$errors[0].$message"
|
:error="
|
||||||
|
v$.mail_sender_id.$error && v$.mail_sender_id.$errors[0].$message
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<BaseInput
|
<BaseMultiselect
|
||||||
v-model="invoiceMailForm.from"
|
v-model="invoiceMailForm.mail_sender_id"
|
||||||
type="text"
|
:invalid="v$.mail_sender_id.$error"
|
||||||
:invalid="v$.from.$error"
|
label="name"
|
||||||
@input="v$.from.$touch()"
|
:options="mailSenders"
|
||||||
|
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
|
||||||
@ -65,6 +75,45 @@
|
|||||||
</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"
|
||||||
>
|
>
|
||||||
@ -77,6 +126,7 @@
|
|||||||
{{ $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"
|
||||||
@ -154,18 +204,24 @@ 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 { useMailDriverStore } from '@/scripts/admin/stores/mail-driver'
|
import { useMailSenderStore } from '@/scripts/admin/stores/mail-sender'
|
||||||
|
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 mailDriverStore = useMailDriverStore()
|
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 emit = defineEmits(['update'])
|
const emit = defineEmits(['update'])
|
||||||
|
|
||||||
@ -179,7 +235,7 @@ const invoiceMailFields = ref([
|
|||||||
|
|
||||||
const invoiceMailForm = reactive({
|
const invoiceMailForm = reactive({
|
||||||
id: null,
|
id: null,
|
||||||
from: null,
|
mail_sender_id: null,
|
||||||
to: null,
|
to: null,
|
||||||
subject: 'New Invoice',
|
subject: 'New Invoice',
|
||||||
body: null,
|
body: null,
|
||||||
@ -198,9 +254,8 @@ const modalData = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
from: {
|
mail_sender_id: {
|
||||||
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),
|
||||||
@ -224,19 +279,25 @@ 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() {
|
||||||
@ -287,4 +348,18 @@ 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,18 +15,28 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<form v-if="!isPreview" action="">
|
<form v-if="!isPreview" action="">
|
||||||
<div class="px-8 py-8 sm:p-6">
|
<!-- v-if -->
|
||||||
|
<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="$t('general.from')"
|
:label="$tc('settings.mail_sender.title', 1)"
|
||||||
required
|
required
|
||||||
:error="v$.from.$error && v$.from.$errors[0].$message"
|
:error="
|
||||||
|
v$.mail_sender_id.$error && v$.mail_sender_id.$errors[0].$message
|
||||||
|
"
|
||||||
>
|
>
|
||||||
<BaseInput
|
<BaseMultiselect
|
||||||
v-model="paymentMailForm.from"
|
v-model="paymentMailForm.mail_sender_id"
|
||||||
type="text"
|
:invalid="v$.mail_sender_id.$error"
|
||||||
:invalid="v$.from.$error"
|
label="name"
|
||||||
@input="v$.from.$touch()"
|
:options="mailSenders"
|
||||||
|
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
|
||||||
@ -65,6 +75,45 @@
|
|||||||
</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"
|
||||||
>
|
>
|
||||||
@ -77,6 +126,7 @@
|
|||||||
{{ $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"
|
||||||
@ -154,20 +204,26 @@ 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',
|
||||||
@ -179,7 +235,7 @@ const paymentMailFields = ref([
|
|||||||
|
|
||||||
const paymentMailForm = reactive({
|
const paymentMailForm = reactive({
|
||||||
id: null,
|
id: null,
|
||||||
from: null,
|
mail_sender_id: null,
|
||||||
to: null,
|
to: null,
|
||||||
subject: 'New Payment',
|
subject: 'New Payment',
|
||||||
body: null,
|
body: null,
|
||||||
@ -198,9 +254,8 @@ const modalData = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
from: {
|
mail_sender_id: {
|
||||||
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),
|
||||||
@ -221,18 +276,25 @@ 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() {
|
||||||
@ -280,4 +342,18 @@ 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"
|
class="block w-8 h-8 rounded md:h-9 md:w-9 object-cover"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@ -1,146 +0,0 @@
|
|||||||
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)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})()
|
|
||||||
}
|
|
||||||
202
resources/scripts/admin/stores/mail-sender.js
Normal file
202
resources/scripts/admin/stores/mail-sender.js
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
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,6 +25,7 @@ export const useUsersStore = (useWindow = false) => {
|
|||||||
password: null,
|
password: null,
|
||||||
phone: null,
|
phone: null,
|
||||||
companies: [],
|
companies: [],
|
||||||
|
sender_id: null,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@ -64,6 +64,13 @@ 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,5 +15,6 @@ export default function () {
|
|||||||
customFields: [],
|
customFields: [],
|
||||||
fields: [],
|
fields: [],
|
||||||
enable_portal: false,
|
enable_portal: false,
|
||||||
|
mail_sender_id: null,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
31
resources/scripts/admin/stub/mail-sender.js
Normal file
31
resources/scripts/admin/stub/mail-sender.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
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,6 +256,7 @@
|
|||||||
/> </template
|
/> </template
|
||||||
></BaseInput>
|
></BaseInput>
|
||||||
</BaseInputGroup>
|
</BaseInputGroup>
|
||||||
|
<!-- && setPasswordMethod !== 'manual' -->
|
||||||
</BaseInputGrid>
|
</BaseInputGrid>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -650,10 +651,7 @@ const rules = computed(() => {
|
|||||||
},
|
},
|
||||||
|
|
||||||
email: {
|
email: {
|
||||||
required: helpers.withMessage(
|
required: helpers.withMessage(t('validation.required'), required),
|
||||||
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,75 +3,238 @@
|
|||||||
: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="next">
|
<form action="" @submit.prevent="submitMailSenderData">
|
||||||
<component
|
<div class="p-4 sm:p-6 my-2">
|
||||||
:is="mailDriverStore.mail_driver"
|
<!-- Name -->
|
||||||
:config-data="mailDriverStore.mailConfigData"
|
<BaseInputGrid>
|
||||||
:is-saving="isSaving"
|
<BaseInputGroup
|
||||||
:is-fetching-initial-data="isFetchingInitialData"
|
:label="$t(`${pre_t}.name`)"
|
||||||
@on-change-driver="(val) => changeDriver(val)"
|
:error="v$.name.$error && v$.name.$errors[0].$message"
|
||||||
@submit-data="next"
|
: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="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>
|
<script setup>
|
||||||
import Smtp from './mail-driver/SmtpMailDriver.vue'
|
import { computed, ref, inject, onMounted } from 'vue'
|
||||||
import Mailgun from './mail-driver/MailgunMailDriver.vue'
|
import { useI18n } from 'vue-i18n'
|
||||||
import Ses from './mail-driver/SesMailDriver.vue'
|
import { useMailSenderStore } from '@/scripts/admin/stores/mail-sender'
|
||||||
import Basic from './mail-driver/BasicMailDriver.vue'
|
import { useVuelidate } from '@vuelidate/core'
|
||||||
import { useMailDriverStore } from '@/scripts/admin/stores/mail-driver'
|
import { required, email, minLength, helpers } from '@vuelidate/validators'
|
||||||
import { ref } from 'vue'
|
import MailSenderDropdown from '@/scripts/admin/components/dropdowns/MailSenderIndexDropdown.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'
|
||||||
|
|
||||||
export default {
|
const pre_t = 'settings.mail_sender'
|
||||||
components: {
|
const mailSenderStore = useMailSenderStore()
|
||||||
Smtp,
|
const { t } = useI18n()
|
||||||
Mailgun,
|
const table = ref(null)
|
||||||
Ses,
|
const utils = inject('utils')
|
||||||
sendmail: Basic,
|
let isSaving = ref(false)
|
||||||
Mail: Basic,
|
const emit = defineEmits(['next'])
|
||||||
},
|
|
||||||
|
|
||||||
emits: ['next'],
|
const loadMailDriver = computed(() => {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
setup(props, { emit }) {
|
// This is multiple email custom validation
|
||||||
const isSaving = ref(false)
|
const multiEmail = (value) => {
|
||||||
const isFetchingInitialData = ref(false)
|
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 mailDriverStore = useMailDriverStore()
|
const emailArr = value.split(',')
|
||||||
|
let isValid = emailArr.every((v) => {
|
||||||
mailDriverStore.mail_driver = 'mail'
|
return emailRegex.test(v)
|
||||||
|
})
|
||||||
loadData()
|
return isValid
|
||||||
|
|
||||||
function changeDriver(value) {
|
|
||||||
mailDriverStore.mail_driver = value
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadData() {
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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 () => {
|
||||||
|
await mailSenderStore.fetchMailDrivers()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -54,8 +54,6 @@
|
|||||||
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"
|
||||||
|
|||||||
@ -1,158 +0,0 @@
|
|||||||
<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>
|
|
||||||
@ -1,247 +0,0 @@
|
|||||||
<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>
|
|
||||||
@ -1,294 +0,0 @@
|
|||||||
<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>
|
|
||||||
@ -1,275 +0,0 @@
|
|||||||
<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>
|
|
||||||
136
resources/scripts/admin/views/settings/mail-sender/Index.vue
Normal file
136
resources/scripts/admin/views/settings/mail-sender/Index.vue
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
<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>
|
||||||
@ -0,0 +1,104 @@
|
|||||||
|
<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>
|
||||||
143
resources/scripts/admin/views/settings/mail-sender/SesDriver.vue
Normal file
143
resources/scripts/admin/views/settings/mail-sender/SesDriver.vue
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
<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>
|
||||||
@ -0,0 +1,120 @@
|
|||||||
|
<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 } from 'vue'
|
import { ref, computed, reactive, onMounted } 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,7 +54,6 @@
|
|||||||
bg-white
|
bg-white
|
||||||
rounded-lg
|
rounded-lg
|
||||||
text-left
|
text-left
|
||||||
overflow-hidden
|
|
||||||
relative
|
relative
|
||||||
shadow-xl
|
shadow-xl
|
||||||
transition-all
|
transition-all
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
"settings": "Nastavení",
|
"settings": "Nastavení",
|
||||||
"logout": "Odhlásit se",
|
"logout": "Odhlásit se",
|
||||||
"users": "Uživatelé",
|
"users": "Uživatelé",
|
||||||
"modules": "Modules"
|
"modules": "Moduly"
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"add_company": "Přidat firmu",
|
"add_company": "Přidat firmu",
|
||||||
@ -93,14 +93,14 @@
|
|||||||
"no_note_found": "Nebyly nalezeny žádné poznámky",
|
"no_note_found": "Nebyly nalezeny žádné poznámky",
|
||||||
"insert_note": "Vložit poznámku",
|
"insert_note": "Vložit poznámku",
|
||||||
"copied_pdf_url_clipboard": "Adresa PDF zkopírována do schránky!",
|
"copied_pdf_url_clipboard": "Adresa PDF zkopírována do schránky!",
|
||||||
"copied_url_clipboard": "Copied url to clipboard!",
|
"copied_url_clipboard": "Zkopírováno do schránky!",
|
||||||
"docs": "Dokumentace",
|
"docs": "Dokumentace",
|
||||||
"do_you_wish_to_continue": "Přejete si pokračovat?",
|
"do_you_wish_to_continue": "Přejete si pokračovat?",
|
||||||
"note": "Poznámka",
|
"note": "Poznámka",
|
||||||
"pay_invoice": "Pay Invoice",
|
"pay_invoice": "Zaplatit fakturu",
|
||||||
"login_successfully": "Logged in successfully!",
|
"login_successfully": "Přihlášení proběhlo úspěšně!",
|
||||||
"logged_out_successfully": "Logged out successfully",
|
"logged_out_successfully": "Odhlášení proběhlo úspěšně",
|
||||||
"mark_as_default": "Mark as default"
|
"mark_as_default": "Označit jako výchozí"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"select_year": "Vybrat rok",
|
"select_year": "Vybrat rok",
|
||||||
@ -108,8 +108,8 @@
|
|||||||
"due_amount": "Částka k zaplacení",
|
"due_amount": "Částka k zaplacení",
|
||||||
"customers": "Zákazníci",
|
"customers": "Zákazníci",
|
||||||
"invoices": "Faktury",
|
"invoices": "Faktury",
|
||||||
"estimates": "Odhady",
|
"estimates": "Nabídky",
|
||||||
"payments": "Payments"
|
"payments": "Platby"
|
||||||
},
|
},
|
||||||
"chart_info": {
|
"chart_info": {
|
||||||
"total_sales": "Slevy",
|
"total_sales": "Slevy",
|
||||||
@ -187,7 +187,7 @@
|
|||||||
"website": "Webová stránka",
|
"website": "Webová stránka",
|
||||||
"overview": "Přehled",
|
"overview": "Přehled",
|
||||||
"invoice_prefix": "Prefix pro faktury",
|
"invoice_prefix": "Prefix pro faktury",
|
||||||
"estimate_prefix": "Prefix pro odhady",
|
"estimate_prefix": "Prefix pro nabídky",
|
||||||
"payment_prefix": "Prefix pro platby",
|
"payment_prefix": "Prefix pro platby",
|
||||||
"enable_portal": "Povolit portál",
|
"enable_portal": "Povolit portál",
|
||||||
"country": "Země",
|
"country": "Země",
|
||||||
@ -208,10 +208,10 @@
|
|||||||
"new_customer": "Nový zákazník",
|
"new_customer": "Nový zákazník",
|
||||||
"edit_customer": "Upravit zákazníka",
|
"edit_customer": "Upravit zákazníka",
|
||||||
"basic_info": "Základní informace",
|
"basic_info": "Základní informace",
|
||||||
"portal_access": "Portal Access",
|
"portal_access": "Přístup do portálu",
|
||||||
"portal_access_text": "Would you like to allow this customer to login to the Customer Portal?",
|
"portal_access_text": "Chcete povolit tomuto zákazníkovi možnost přihlásit se na zákaznický portál?",
|
||||||
"portal_access_url": "Customer Portal Login URL",
|
"portal_access_url": "URL pro přihlášení do zákaznického portálu",
|
||||||
"portal_access_url_help": "Please copy & forward the above given URL to your customer for providing access.",
|
"portal_access_url_help": "Zkopírujte a pošlete výše uvedenou adresu URL vašemu zákazníkovi pro poskytnutí přístupu.",
|
||||||
"billing_address": "Fakturační adresa",
|
"billing_address": "Fakturační adresa",
|
||||||
"shipping_address": "Doručovací adresa",
|
"shipping_address": "Doručovací adresa",
|
||||||
"copy_billing_address": "Zkopírovat z fakturace",
|
"copy_billing_address": "Zkopírovat z fakturace",
|
||||||
@ -231,7 +231,7 @@
|
|||||||
"confirm_delete": "Nebudete moci obnovit tohoto zákazníka a všechny jeho faktury, odhady a platby. | Nebudete moci obnovit tyto zákazníky a všechny jejich faktury, odhady a platby.",
|
"confirm_delete": "Nebudete moci obnovit tohoto zákazníka a všechny jeho faktury, odhady a platby. | Nebudete moci obnovit tyto zákazníky a všechny jejich faktury, odhady a platby.",
|
||||||
"created_message": "Zákazník úspěšně vytvořen",
|
"created_message": "Zákazník úspěšně vytvořen",
|
||||||
"updated_message": "Zákazník úspěšně upraven",
|
"updated_message": "Zákazník úspěšně upraven",
|
||||||
"address_updated_message": "Address Information Updated succesfully",
|
"address_updated_message": "Adresa úspěšně aktualizována",
|
||||||
"deleted_message": "Zákazník úspěšně smazán | Zákazníci úspěšně smazáni",
|
"deleted_message": "Zákazník úspěšně smazán | Zákazníci úspěšně smazáni",
|
||||||
"edit_currency_not_allowed": "Po vytvoření transakce nelze změnit měnu."
|
"edit_currency_not_allowed": "Po vytvoření transakce nelze změnit měnu."
|
||||||
},
|
},
|
||||||
@ -264,11 +264,11 @@
|
|||||||
"deleted_message": "Položka byla úspěšně odstraněna | Položky byly úspěšně odstraněny"
|
"deleted_message": "Položka byla úspěšně odstraněna | Položky byly úspěšně odstraněny"
|
||||||
},
|
},
|
||||||
"estimates": {
|
"estimates": {
|
||||||
"title": "Odhady",
|
"title": "Nabídky",
|
||||||
"accept_estimate": "Accept Estimate",
|
"accept_estimate": "Přijmout nabídku",
|
||||||
"reject_estimate": "Reject Estimate",
|
"reject_estimate": "Odmítnout nabídku",
|
||||||
"estimate": "Odhad | Odhady",
|
"estimate": "Nabídka | Nabídky",
|
||||||
"estimates_list": "Seznam odhadů",
|
"estimates_list": "Seznam nabídek",
|
||||||
"days": "{days} dní",
|
"days": "{days} dní",
|
||||||
"months": "{months} měsíc",
|
"months": "{months} měsíc",
|
||||||
"years": "{years} rok",
|
"years": "{years} rok",
|
||||||
@ -283,7 +283,7 @@
|
|||||||
"total": "Celkem",
|
"total": "Celkem",
|
||||||
"discount": "Sleva",
|
"discount": "Sleva",
|
||||||
"sub_total": "Mezisoučet",
|
"sub_total": "Mezisoučet",
|
||||||
"estimate_number": "Odhadované číslo",
|
"estimate_number": "Číslo nabídky",
|
||||||
"ref_number": "Referenční číslo",
|
"ref_number": "Referenční číslo",
|
||||||
"contact": "Kontakt",
|
"contact": "Kontakt",
|
||||||
"add_item": "Přidat položku",
|
"add_item": "Přidat položku",
|
||||||
@ -299,11 +299,11 @@
|
|||||||
"estimate_template": "Šablona",
|
"estimate_template": "Šablona",
|
||||||
"convert_to_invoice": "Převést na fakturu",
|
"convert_to_invoice": "Převést na fakturu",
|
||||||
"mark_as_sent": "Označit jako odeslané",
|
"mark_as_sent": "Označit jako odeslané",
|
||||||
"send_estimate": "Odeslat odhad",
|
"send_estimate": "Odeslat nabídku",
|
||||||
"resend_estimate": "Znovu odeslat odhad",
|
"resend_estimate": "Znovu odeslat nabídku",
|
||||||
"record_payment": "Zaznamenat platbu",
|
"record_payment": "Zaznamenat platbu",
|
||||||
"add_estimate": "Přidat odhad",
|
"add_estimate": "Přidat nabídku",
|
||||||
"save_estimate": "Uložit odhad",
|
"save_estimate": "Uložit nabídku",
|
||||||
"confirm_conversion": "Tento odhad bude použit k vytvoření nové faktury.",
|
"confirm_conversion": "Tento odhad bude použit k vytvoření nové faktury.",
|
||||||
"conversion_message": "Faktura byla úspěšně vytvořena",
|
"conversion_message": "Faktura byla úspěšně vytvořena",
|
||||||
"confirm_send_estimate": "Tento odhad bude zaslán e-mailem zákazníkovi",
|
"confirm_send_estimate": "Tento odhad bude zaslán e-mailem zákazníkovi",
|
||||||
@ -318,10 +318,10 @@
|
|||||||
},
|
},
|
||||||
"accepted": "Přijato",
|
"accepted": "Přijato",
|
||||||
"rejected": "Odmítnuto",
|
"rejected": "Odmítnuto",
|
||||||
"expired": "Expired",
|
"expired": "Vypršela platnost",
|
||||||
"sent": "Odesláno",
|
"sent": "Odesláno",
|
||||||
"draft": "Koncept",
|
"draft": "Koncept",
|
||||||
"viewed": "Viewed",
|
"viewed": "Zobrazené",
|
||||||
"declined": "Odmítnuto",
|
"declined": "Odmítnuto",
|
||||||
"new_estimate": "Nový odhad",
|
"new_estimate": "Nový odhad",
|
||||||
"add_new_estimate": "Přidat nový odhad",
|
"add_new_estimate": "Přidat nový odhad",
|
||||||
@ -355,14 +355,14 @@
|
|||||||
"select_an_item": "Pište nebo klikněte pro výběr položky",
|
"select_an_item": "Pište nebo klikněte pro výběr položky",
|
||||||
"type_item_description": "Zadejte popis položky (volitelné)"
|
"type_item_description": "Zadejte popis položky (volitelné)"
|
||||||
},
|
},
|
||||||
"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 povoleno, bude vybraná šablona automaticky vybrána pro nové nabídky."
|
||||||
},
|
},
|
||||||
"invoices": {
|
"invoices": {
|
||||||
"title": "Faktury",
|
"title": "Faktury",
|
||||||
"download": "Download",
|
"download": "Stáhnout",
|
||||||
"pay_invoice": "Pay Invoice",
|
"pay_invoice": "Zaplatit fakturu",
|
||||||
"invoices_list": "Seznam faktur",
|
"invoices_list": "Seznam faktur",
|
||||||
"invoice_information": "Invoice Information",
|
"invoice_information": "Informace o faktuře",
|
||||||
"days": "{days} dní",
|
"days": "{days} dní",
|
||||||
"months": "{months} měsíc",
|
"months": "{months} měsíc",
|
||||||
"years": "{years} rok",
|
"years": "{years} rok",
|
||||||
@ -447,7 +447,7 @@
|
|||||||
"marked_as_sent_message": "Faktura označena jako úspěšně odeslaná",
|
"marked_as_sent_message": "Faktura označena jako úspěšně odeslaná",
|
||||||
"something_went_wrong": "něco se nezdařilo",
|
"something_went_wrong": "něco se nezdařilo",
|
||||||
"invalid_due_amount_message": "Celková částka faktury nemůže být nižší než celková částka zaplacená za tuto fakturu. Chcete-li pokračovat, upravte fakturu nebo smažte související platby.",
|
"invalid_due_amount_message": "Celková částka faktury nemůže být nižší než celková částka zaplacená za tuto fakturu. Chcete-li pokračovat, upravte fakturu nebo smažte související platby.",
|
||||||
"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 povoleno, bude vybraná šablona automaticky vybrána pro nové faktury."
|
||||||
},
|
},
|
||||||
"recurring_invoices": {
|
"recurring_invoices": {
|
||||||
"title": "Opakující se faktury",
|
"title": "Opakující se faktury",
|
||||||
@ -526,7 +526,7 @@
|
|||||||
"cloned_successfully": "Opakující se faktura úspěšně naklonována",
|
"cloned_successfully": "Opakující se faktura úspěšně naklonována",
|
||||||
"clone_invoice": "Naklonovat opakující se fakturu",
|
"clone_invoice": "Naklonovat opakující se fakturu",
|
||||||
"confirm_clone": "Tato opakující se faktura bude naklonována do nové opakující se faktury",
|
"confirm_clone": "Tato opakující se faktura bude naklonována do nové opakující se faktury",
|
||||||
"add_customer_email": "Please add an email address for this customer to send invoices automatically.",
|
"add_customer_email": "Pro automatické odesílání faktur prosím přidejte e-mailovou adresu tohoto zákazníka.",
|
||||||
"item": {
|
"item": {
|
||||||
"title": "Název položky",
|
"title": "Název položky",
|
||||||
"description": "Popis",
|
"description": "Popis",
|
||||||
@ -658,49 +658,49 @@
|
|||||||
"retype_password": "Zadejte heslo znovu"
|
"retype_password": "Zadejte heslo znovu"
|
||||||
},
|
},
|
||||||
"modules": {
|
"modules": {
|
||||||
"buy_now": "Buy Now",
|
"buy_now": "Koupit",
|
||||||
"install": "Install",
|
"install": "Instalovat",
|
||||||
"price": "Price",
|
"price": "Cena",
|
||||||
"download_zip_file": "Download ZIP file",
|
"download_zip_file": "Stáhnout soubor ZIP",
|
||||||
"unzipping_package": "Unzipping Package",
|
"unzipping_package": "Rozbalování balíku",
|
||||||
"copying_files": "Copying Files",
|
"copying_files": "Kopírování souborů",
|
||||||
"deleting_files": "Deleting Unused files",
|
"deleting_files": "Odstraňování nepoužitých souborů",
|
||||||
"completing_installation": "Completing Installation",
|
"completing_installation": "Dokončování instalace",
|
||||||
"update_failed": "Update Failed",
|
"update_failed": "Aktualizace se nezdařila",
|
||||||
"install_success": "Module has been installed successfully!",
|
"install_success": "Modul byl úspěšně nainstalován!",
|
||||||
"customer_reviews": "Reviews",
|
"customer_reviews": "Recenze",
|
||||||
"license": "License",
|
"license": "Licence",
|
||||||
"faq": "FAQ",
|
"faq": "Často kladené dotazy (FAQ)",
|
||||||
"monthly": "Monthly",
|
"monthly": "Měsíčně",
|
||||||
"yearly": "Yearly",
|
"yearly": "Ročně",
|
||||||
"updated": "Updated",
|
"updated": "Aktualizováno",
|
||||||
"version": "Version",
|
"version": "Verze",
|
||||||
"disable": "Disable",
|
"disable": "Zakázat",
|
||||||
"module_disabled": "Module Disabled",
|
"module_disabled": "Modul zakázán",
|
||||||
"enable": "Enable",
|
"enable": "Povolit",
|
||||||
"module_enabled": "Module Enabled",
|
"module_enabled": "Modul povolen",
|
||||||
"update_to": "Update To",
|
"update_to": "Aktualizovat na",
|
||||||
"module_updated": "Module Updated Successfully!",
|
"module_updated": "Modul byl úspěšně aktualizován!",
|
||||||
"title": "Modules",
|
"title": "Moduly",
|
||||||
"module": "Module | Modules",
|
"module": "Modul | Moduly",
|
||||||
"api_token": "API token",
|
"api_token": "API token",
|
||||||
"invalid_api_token": "Invalid API Token.",
|
"invalid_api_token": "Neplatný API token.",
|
||||||
"other_modules": "Other Modules",
|
"other_modules": "Další moduly",
|
||||||
"view_all": "View All",
|
"view_all": "Zobrazit vše",
|
||||||
"no_reviews_found": "There are no reviews for this module yet!",
|
"no_reviews_found": "Pro tento modul zatím neexistují žádné recenze!",
|
||||||
"module_not_purchased": "Module Not Purchased",
|
"module_not_purchased": "Modul není zakoupený",
|
||||||
"module_not_found": "Module Not Found",
|
"module_not_found": "Modul nebyl nalezen",
|
||||||
"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 aktualizováno",
|
||||||
"connect_installation": "Connect your installation",
|
"connect_installation": "Připojte vaši instalaci",
|
||||||
"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": "Přihlaste se k {url} a připojte tuto instalaci zadáním API tokenu. Vaše zakoupené moduly se zde zobrazí po navázání připojení.",
|
||||||
"view_module": "View Module",
|
"view_module": "Zobrazit modul",
|
||||||
"update_available": "Update Available",
|
"update_available": "Je k dispozici aktualizace",
|
||||||
"purchased": "Purchased",
|
"purchased": "Zakoupeno",
|
||||||
"installed": "Installed",
|
"installed": "Nainstalováno",
|
||||||
"no_modules_installed": "No Modules Installed Yet!",
|
"no_modules_installed": "Nejsou nainstalovány žádné moduly!",
|
||||||
"disable_warning": "All the settings for this particular will be reverted.",
|
"disable_warning": "Všechna nastavení pro tuto konkrétní položku budou vrácena zpět.",
|
||||||
"what_you_get": "What you get"
|
"what_you_get": "Co získáte"
|
||||||
},
|
},
|
||||||
"users": {
|
"users": {
|
||||||
"title": "Uživatelé",
|
"title": "Uživatelé",
|
||||||
@ -731,7 +731,7 @@
|
|||||||
"companies": "Společnosti"
|
"companies": "Společnosti"
|
||||||
},
|
},
|
||||||
"reports": {
|
"reports": {
|
||||||
"title": "Report",
|
"title": "Hlášení",
|
||||||
"from_date": "Datum od",
|
"from_date": "Datum od",
|
||||||
"to_date": "Do data",
|
"to_date": "Do data",
|
||||||
"status": "Stav",
|
"status": "Stav",
|
||||||
@ -739,8 +739,8 @@
|
|||||||
"unpaid": "Nezaplaceno",
|
"unpaid": "Nezaplaceno",
|
||||||
"download_pdf": "Stáhnout PDF",
|
"download_pdf": "Stáhnout PDF",
|
||||||
"view_pdf": "Zobrazit PDF",
|
"view_pdf": "Zobrazit PDF",
|
||||||
"update_report": "Upravit report",
|
"update_report": "Upravit hlášení",
|
||||||
"report": "Report | Reporty",
|
"report": "Hlášení | Hlášení",
|
||||||
"profit_loss": {
|
"profit_loss": {
|
||||||
"profit_loss": "Zisk a ztráta",
|
"profit_loss": "Zisk a ztráta",
|
||||||
"to_date": "Do data",
|
"to_date": "Do data",
|
||||||
@ -752,7 +752,7 @@
|
|||||||
"date_range": "Vybrat časový rozsah",
|
"date_range": "Vybrat časový rozsah",
|
||||||
"to_date": "Do data",
|
"to_date": "Do data",
|
||||||
"from_date": "Od data",
|
"from_date": "Od data",
|
||||||
"report_type": "Typ reportu"
|
"report_type": "Typ hlášení"
|
||||||
},
|
},
|
||||||
"taxes": {
|
"taxes": {
|
||||||
"taxes": "Daně",
|
"taxes": "Daně",
|
||||||
@ -807,10 +807,10 @@
|
|||||||
"payment_modes": "Způsoby plateb",
|
"payment_modes": "Způsoby plateb",
|
||||||
"notes": "Poznámky",
|
"notes": "Poznámky",
|
||||||
"exchange_rate": "Směnný kurz",
|
"exchange_rate": "Směnný kurz",
|
||||||
"address_information": "Address Information"
|
"address_information": "Adresa"
|
||||||
},
|
},
|
||||||
"address_information": {
|
"address_information": {
|
||||||
"section_description": " You can update Your Address information using form below."
|
"section_description": " Adresu můžete aktualizovat pomocí formuláře níže."
|
||||||
},
|
},
|
||||||
"title": "Nastavení",
|
"title": "Nastavení",
|
||||||
"setting": "Nastavení | Nastavení",
|
"setting": "Nastavení | Nastavení",
|
||||||
@ -1114,7 +1114,7 @@
|
|||||||
"exchange_help_text": "Zadejte směnný kurz pro převod z {currency} do {baseCurrency}",
|
"exchange_help_text": "Zadejte směnný kurz pro převod z {currency} do {baseCurrency}",
|
||||||
"currency_freak": "Currency Freak",
|
"currency_freak": "Currency Freak",
|
||||||
"currency_layer": "Currency Layer",
|
"currency_layer": "Currency Layer",
|
||||||
"open_exchange_rate": "Open Exchange Rate",
|
"open_exchange_rate": "Otevřít směnný kurz",
|
||||||
"currency_converter": "Převodník měn",
|
"currency_converter": "Převodník měn",
|
||||||
"server": "Server",
|
"server": "Server",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
@ -1150,8 +1150,8 @@
|
|||||||
"payment_mode_added": "Platební metoda přidána",
|
"payment_mode_added": "Platební metoda přidána",
|
||||||
"payment_mode_updated": "Platební metoda upravena",
|
"payment_mode_updated": "Platební metoda upravena",
|
||||||
"payment_mode_confirm_delete": "Nebudete moci obnovit tuto platební metodu",
|
"payment_mode_confirm_delete": "Nebudete moci obnovit tuto platební metodu",
|
||||||
"payments_attached": "This payment method is already attached to payments. Please delete the attached payments to proceed with deletion.",
|
"payments_attached": "Tento způsob platby je již připojen k platbám. Chcete-li pokračovat v odstranění, odstraňte připojené platby.",
|
||||||
"expenses_attached": "This payment method is already attached to expenses. Please delete the attached expenses to proceed with deletion.",
|
"expenses_attached": "Tento způsob platby je již připojen k výdajům. Chcete-li pokračovat v odstranění, odstraňte připojené výdaje.",
|
||||||
"deleted_message": "Platební metoda byla úspěšně odstraněna"
|
"deleted_message": "Platební metoda byla úspěšně odstraněna"
|
||||||
},
|
},
|
||||||
"expense_category": {
|
"expense_category": {
|
||||||
@ -1178,8 +1178,8 @@
|
|||||||
"discount_setting": "Nastavení slev",
|
"discount_setting": "Nastavení slev",
|
||||||
"discount_per_item": "Sleva za položku ",
|
"discount_per_item": "Sleva za položku ",
|
||||||
"discount_setting_description": "Povolte tuto možnost, pokud chcete přidat slevu do jednotlivých položek faktury. Ve výchozím nastavení je sleva přidána přímo na fakturu.",
|
"discount_setting_description": "Povolte tuto možnost, pokud chcete přidat slevu do jednotlivých položek faktury. Ve výchozím nastavení je sleva přidána přímo na fakturu.",
|
||||||
"expire_public_links": "Automatically Expire Public Links",
|
"expire_public_links": "Automaticky zrušit platnost veřejných odkazů",
|
||||||
"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": "Určete, zda chcete zrušit všechny odkazy odeslané aplikací k zobrazení faktur, odhadů, plateb atd. po stanovené době trvání.",
|
||||||
"save": "Uložit",
|
"save": "Uložit",
|
||||||
"preference": "Předvolba | Předvolby",
|
"preference": "Předvolba | Předvolby",
|
||||||
"general_settings": "Výchozí předvolby systému.",
|
"general_settings": "Výchozí předvolby systému.",
|
||||||
@ -1301,16 +1301,16 @@
|
|||||||
"invalid_disk_credentials": "Nesprávné přihlašovací údaje pro vybraný disk"
|
"invalid_disk_credentials": "Nesprávné přihlašovací údaje pro vybraný disk"
|
||||||
},
|
},
|
||||||
"taxations": {
|
"taxations": {
|
||||||
"add_billing_address": "Enter Billing Address",
|
"add_billing_address": "Zadejte fakturační adresu",
|
||||||
"add_shipping_address": "Enter Shipping Address",
|
"add_shipping_address": "Zadejte doručovací adresu",
|
||||||
"add_company_address": "Enter Company Address",
|
"add_company_address": "Zadejte adresu firmy",
|
||||||
"modal_description": "The information below is required in order to fetch sales tax.",
|
"modal_description": "Níže uvedené informace jsou vyžadovány pro načtení daně z prodeje.",
|
||||||
"add_address": "Add Address for fetching sales tax.",
|
"add_address": "Přidat adresu pro načtení daně z prodeje.",
|
||||||
"address_placeholder": "Example: 123, My Street",
|
"address_placeholder": "Například: Moje Ulice 123",
|
||||||
"city_placeholder": "Example: Los Angeles",
|
"city_placeholder": "Například: Praha",
|
||||||
"state_placeholder": "Example: CA",
|
"state_placeholder": "Například: CZ",
|
||||||
"zip_placeholder": "Example: 90024",
|
"zip_placeholder": "Například: 90024",
|
||||||
"invalid_address": "Please provide valid address details."
|
"invalid_address": "Zadejte prosím platnou adresu."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"wizard": {
|
"wizard": {
|
||||||
@ -1470,17 +1470,17 @@
|
|||||||
"not_allowed": "Není povoleno",
|
"not_allowed": "Není povoleno",
|
||||||
"login_invalid_credentials": "Tyto údaje neodpovídají našim záznamům.",
|
"login_invalid_credentials": "Tyto údaje neodpovídají našim záznamům.",
|
||||||
"enter_valid_cron_format": "Zadejte platný formát cronu",
|
"enter_valid_cron_format": "Zadejte platný formát cronu",
|
||||||
"email_could_not_be_sent": "Email could not be sent to this email address.",
|
"email_could_not_be_sent": "E-mail nemohl být odeslán na tuto e-mailovou adresu.",
|
||||||
"invalid_address": "Please enter a valid address.",
|
"invalid_address": "Zadejte prosím platnou adresu.",
|
||||||
"invalid_key": "Please enter valid key.",
|
"invalid_key": "Zadejte prosím platný klíč.",
|
||||||
"invalid_state": "Please enter a valid state.",
|
"invalid_state": "Zadejte prosím platný název státu.",
|
||||||
"invalid_city": "Please enter a valid city.",
|
"invalid_city": "Zadejte prosím platný název města.",
|
||||||
"invalid_postal_code": "Please enter a valid zip.",
|
"invalid_postal_code": "Zadejte prosím platné PSČ.",
|
||||||
"invalid_format": "Please enter valid query string format.",
|
"invalid_format": "Zadejte prosím data v platném formátu.",
|
||||||
"api_error": "Server not responding.",
|
"api_error": "Server neodpovídá.",
|
||||||
"feature_not_enabled": "Feature not enabled.",
|
"feature_not_enabled": "Funkce není zapnuta.",
|
||||||
"request_limit_met": "Api request limit exceeded.",
|
"request_limit_met": "Limit požadavků API překročen.",
|
||||||
"address_incomplete": "Incomplete Address"
|
"address_incomplete": "Neúplná adresa"
|
||||||
},
|
},
|
||||||
"pdf_estimate_label": "Odhad",
|
"pdf_estimate_label": "Odhad",
|
||||||
"pdf_estimate_number": "Číslo odhadu",
|
"pdf_estimate_number": "Číslo odhadu",
|
||||||
@ -1504,18 +1504,18 @@
|
|||||||
"pdf_payment_number": "Číslo platby",
|
"pdf_payment_number": "Číslo platby",
|
||||||
"pdf_payment_mode": "Platební metoda",
|
"pdf_payment_mode": "Platební metoda",
|
||||||
"pdf_payment_amount_received_label": "Obdržená částka",
|
"pdf_payment_amount_received_label": "Obdržená částka",
|
||||||
"pdf_expense_report_label": "REPORT VÝDAJŮ",
|
"pdf_expense_report_label": "HLÁŠENÍ VÝDAJŮ",
|
||||||
"pdf_total_expenses_label": "VÝDAJE CELKEM",
|
"pdf_total_expenses_label": "VÝDAJE CELKEM",
|
||||||
"pdf_profit_loss_label": "REPORT ZISKU A ZTRÁT",
|
"pdf_profit_loss_label": "HLÁŠENÍ ZISKU A ZTRÁT",
|
||||||
"pdf_sales_customers_label": "Report o zákaznících prodeje",
|
"pdf_sales_customers_label": "Hlášení o zákaznících prodeje",
|
||||||
"pdf_sales_items_label": "Report o položkách prodeje",
|
"pdf_sales_items_label": "Hlášení o položkách prodeje",
|
||||||
"pdf_tax_summery_label": "Report o shrnutí daní",
|
"pdf_tax_summery_label": "Hlášení o daních",
|
||||||
"pdf_income_label": "PŘÍJEM",
|
"pdf_income_label": "PŘÍJEM",
|
||||||
"pdf_net_profit_label": "ČISTÝ ZISK",
|
"pdf_net_profit_label": "ČISTÝ ZISK",
|
||||||
"pdf_customer_sales_report": "Report o prodeji: Podle zákazníka",
|
"pdf_customer_sales_report": "Hlášení o prodeji: Podle zákazníka",
|
||||||
"pdf_total_sales_label": "PRODEJE CELKEM",
|
"pdf_total_sales_label": "PRODEJE CELKEM",
|
||||||
"pdf_item_sales_label": "Report o prodeji: Podle položky",
|
"pdf_item_sales_label": "Hlášení o prodeji: Podle položky",
|
||||||
"pdf_tax_report_label": "DAŇOVÝ REPORT",
|
"pdf_tax_report_label": "DAŇOVÉ HLÁŠENÍ",
|
||||||
"pdf_total_tax_label": "DANĚ CELKEM",
|
"pdf_total_tax_label": "DANĚ CELKEM",
|
||||||
"pdf_tax_types_label": "Typy daní",
|
"pdf_tax_types_label": "Typy daní",
|
||||||
"pdf_expenses_label": "Výdaje",
|
"pdf_expenses_label": "Výdaje",
|
||||||
|
|||||||
@ -526,7 +526,7 @@
|
|||||||
"cloned_successfully": "Serienrechnung erfolgreich kopiert",
|
"cloned_successfully": "Serienrechnung erfolgreich kopiert",
|
||||||
"clone_invoice": "Serienrechnung kopieren",
|
"clone_invoice": "Serienrechnung kopieren",
|
||||||
"confirm_clone": "Diese Serienrechnung wird in eine neue Serienrechnung kopiert",
|
"confirm_clone": "Diese Serienrechnung wird in eine neue Serienrechnung kopiert",
|
||||||
"add_customer_email": "Please add an email address for this customer to send invoices automatically.",
|
"add_customer_email": "Bitte fügen Sie eine E-Mail-Adresse für diesen Kunden hinzu, um Rechnungen automatisch zu senden.",
|
||||||
"item": {
|
"item": {
|
||||||
"title": "Titel des Artikels",
|
"title": "Titel des Artikels",
|
||||||
"description": "Beschreibung",
|
"description": "Beschreibung",
|
||||||
@ -682,25 +682,25 @@
|
|||||||
"update_to": "Update auf",
|
"update_to": "Update auf",
|
||||||
"module_updated": "Modul erfolgreich aktualisiert!",
|
"module_updated": "Modul erfolgreich aktualisiert!",
|
||||||
"title": "Module",
|
"title": "Module",
|
||||||
"module": "Module | Modules",
|
"module": "Modul | Module",
|
||||||
"api_token": "API Schlüssel",
|
"api_token": "API Schlüssel",
|
||||||
"invalid_api_token": "Invalid API Token.",
|
"invalid_api_token": "Ungültiger API-Schlüssel.",
|
||||||
"other_modules": "Weitere Module",
|
"other_modules": "Weitere Module",
|
||||||
"view_all": "Alle Anzeigen",
|
"view_all": "Alle Anzeigen",
|
||||||
"no_reviews_found": "There are no reviews for this module yet!",
|
"no_reviews_found": "Für dieses Modul gibt es noch keine Bewertungen!",
|
||||||
"module_not_purchased": "Module Not Purchased",
|
"module_not_purchased": "Module Not Purchased",
|
||||||
"module_not_found": "Module Not Found",
|
"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",
|
||||||
"connect_installation": "Installation verbinden",
|
"connect_installation": "Installation verbinden",
|
||||||
"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": "Melden Sie sich bei {url} an und verbinden Sie diese Installation durch Eingabe des API-Token. Ihre gekauften Module werden hier angezeigt, nachdem die Verbindung hergestellt wurde.",
|
||||||
"view_module": "Modul anzeigen",
|
"view_module": "Modul anzeigen",
|
||||||
"update_available": "Aktualisierung verfügbar",
|
"update_available": "Aktualisierung verfügbar",
|
||||||
"purchased": "Gekauft",
|
"purchased": "Gekauft",
|
||||||
"installed": "Installiert",
|
"installed": "Installiert",
|
||||||
"no_modules_installed": "Noch keine Module installiert!",
|
"no_modules_installed": "Noch keine Module installiert!",
|
||||||
"disable_warning": "All the settings for this particular will be reverted.",
|
"disable_warning": "Alle Einstellungen für diesen speziellen Wert werden zurückgesetzt.",
|
||||||
"what_you_get": "What you get"
|
"what_you_get": "Was Sie erhalten"
|
||||||
},
|
},
|
||||||
"users": {
|
"users": {
|
||||||
"title": "Benutzer",
|
"title": "Benutzer",
|
||||||
@ -1304,8 +1304,8 @@
|
|||||||
"add_billing_address": "Rechnungsadresse eingeben",
|
"add_billing_address": "Rechnungsadresse eingeben",
|
||||||
"add_shipping_address": "Lieferadresse eingeben",
|
"add_shipping_address": "Lieferadresse eingeben",
|
||||||
"add_company_address": "Firmenadresse eingeben",
|
"add_company_address": "Firmenadresse eingeben",
|
||||||
"modal_description": "The information below is required in order to fetch sales tax.",
|
"modal_description": "Die untenstehenden Informationen sind erforderlich, um die Steuer berücksichtigen zu können.",
|
||||||
"add_address": "Add Address for fetching sales tax.",
|
"add_address": "Fügen Sie eine Adresse hinzu, um die Steuer abrufen zu können.",
|
||||||
"address_placeholder": "Beispiel: 123, meine Straße",
|
"address_placeholder": "Beispiel: 123, meine Straße",
|
||||||
"city_placeholder": "Beispiel: Los Angeles",
|
"city_placeholder": "Beispiel: Los Angeles",
|
||||||
"state_placeholder": "Beispiel: CA",
|
"state_placeholder": "Beispiel: CA",
|
||||||
@ -1471,21 +1471,21 @@
|
|||||||
"login_invalid_credentials": "Diese Anmeldeinformationen stimmen nicht mit unseren Aufzeichnungen überein.",
|
"login_invalid_credentials": "Diese Anmeldeinformationen stimmen nicht mit unseren Aufzeichnungen überein.",
|
||||||
"enter_valid_cron_format": "Bitte geben Sie ein gültiges Cron-Format ein",
|
"enter_valid_cron_format": "Bitte geben Sie ein gültiges Cron-Format ein",
|
||||||
"email_could_not_be_sent": "Die E-Mail konnte nicht an diese Adresse gesendet werden.",
|
"email_could_not_be_sent": "Die E-Mail konnte nicht an diese Adresse gesendet werden.",
|
||||||
"invalid_address": "Please enter a valid address.",
|
"invalid_address": "Bitte geben Sie eine gültige Adresse ein.",
|
||||||
"invalid_key": "Please enter valid key.",
|
"invalid_key": "Bitte geben Sie einen gültigen Schlüssel ein.",
|
||||||
"invalid_state": "Please enter a valid state.",
|
"invalid_state": "Bitte geben Sie ein gültiges Bundesland ein.",
|
||||||
"invalid_city": "Please enter a valid city.",
|
"invalid_city": "Bitte geben Sie eine gültige Stadt an.",
|
||||||
"invalid_postal_code": "Please enter a valid zip.",
|
"invalid_postal_code": "Bitte geben Sie eine gültige PLZ an.",
|
||||||
"invalid_format": "Please enter valid query string format.",
|
"invalid_format": "Bitte geben Sie ein gültiges Abfrageformat ein.",
|
||||||
"api_error": "Der Server antwortet nicht.",
|
"api_error": "Der Server antwortet nicht.",
|
||||||
"feature_not_enabled": "Funktion nicht aktiviert.",
|
"feature_not_enabled": "Funktion nicht aktiviert.",
|
||||||
"request_limit_met": "Api request limit exceeded.",
|
"request_limit_met": "Api Anfragelimit überschritten.",
|
||||||
"address_incomplete": "Unvollständige Adresse"
|
"address_incomplete": "Unvollständige Adresse"
|
||||||
},
|
},
|
||||||
"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": "Ablaufdatum",
|
"pdf_estimate_expire_date": "Gültig bis",
|
||||||
"pdf_invoice_label": "Rechnung",
|
"pdf_invoice_label": "Rechnung",
|
||||||
"pdf_invoice_number": "Rechnungsnummer",
|
"pdf_invoice_number": "Rechnungsnummer",
|
||||||
"pdf_invoice_date": "Rechnungsdatum",
|
"pdf_invoice_date": "Rechnungsdatum",
|
||||||
@ -1519,8 +1519,8 @@
|
|||||||
"pdf_total_tax_label": "Gesamte Umsatzsteuer",
|
"pdf_total_tax_label": "Gesamte Umsatzsteuer",
|
||||||
"pdf_tax_types_label": "Steuersätze",
|
"pdf_tax_types_label": "Steuersätze",
|
||||||
"pdf_expenses_label": "Ausgaben",
|
"pdf_expenses_label": "Ausgaben",
|
||||||
"pdf_bill_to": "Rechnungsempfänger:",
|
"pdf_bill_to": "Rechnungsanschrift",
|
||||||
"pdf_ship_to": "Versand an:",
|
"pdf_ship_to": "Lieferanschrift",
|
||||||
"pdf_received_from": "Erhalten von:",
|
"pdf_received_from": "Erhalten von:",
|
||||||
"pdf_tax_label": "Steuer"
|
"pdf_tax_label": "Steuer"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,13 +6,13 @@
|
|||||||
"invoices": "Τιμολόγια",
|
"invoices": "Τιμολόγια",
|
||||||
"recurring-invoices": "Επαναλαμβανόμενα τιμολόγια",
|
"recurring-invoices": "Επαναλαμβανόμενα τιμολόγια",
|
||||||
"expenses": "Έξοδα",
|
"expenses": "Έξοδα",
|
||||||
"estimates": "Εκτιμήσεις",
|
"estimates": "Προσφορές",
|
||||||
"payments": "Πληρωμές",
|
"payments": "Πληρωμές",
|
||||||
"reports": "Αναφορές",
|
"reports": "Αναφορές",
|
||||||
"settings": "Ρυθμίσεις",
|
"settings": "Ρυθμίσεις",
|
||||||
"logout": "Αποσύνδεση",
|
"logout": "Αποσύνδεση",
|
||||||
"users": "Χρήστες",
|
"users": "Χρήστες",
|
||||||
"modules": "Modules"
|
"modules": "Πρόσθετα"
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"add_company": "Προσθήκη Εταιρείας",
|
"add_company": "Προσθήκη Εταιρείας",
|
||||||
@ -93,14 +93,14 @@
|
|||||||
"no_note_found": "Δεν Βρέθηκε Σημείωση",
|
"no_note_found": "Δεν Βρέθηκε Σημείωση",
|
||||||
"insert_note": "Εισαγωγή Σημείωσης",
|
"insert_note": "Εισαγωγή Σημείωσης",
|
||||||
"copied_pdf_url_clipboard": "Αντιγράφηκε το url του PDF στo πρόχειρο!",
|
"copied_pdf_url_clipboard": "Αντιγράφηκε το url του PDF στo πρόχειρο!",
|
||||||
"copied_url_clipboard": "Copied url to clipboard!",
|
"copied_url_clipboard": "Ο σύνδεσμος αντιγράφηκε στο πρόχειρο!",
|
||||||
"docs": "Έγγραφα",
|
"docs": "Έγγραφα",
|
||||||
"do_you_wish_to_continue": "Θέλετε να συνεχίσετε;",
|
"do_you_wish_to_continue": "Θέλετε να συνεχίσετε;",
|
||||||
"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": "Επιλογή έτους",
|
||||||
@ -108,8 +108,8 @@
|
|||||||
"due_amount": "Οφειλόμενο Ποσό",
|
"due_amount": "Οφειλόμενο Ποσό",
|
||||||
"customers": "Πελάτες",
|
"customers": "Πελάτες",
|
||||||
"invoices": "Τιμολόγια",
|
"invoices": "Τιμολόγια",
|
||||||
"estimates": "Εκτιμήσεις",
|
"estimates": "Προσφορές",
|
||||||
"payments": "Payments"
|
"payments": "Πληρωμές"
|
||||||
},
|
},
|
||||||
"chart_info": {
|
"chart_info": {
|
||||||
"total_sales": "Πωλήσεις",
|
"total_sales": "Πωλήσεις",
|
||||||
@ -130,7 +130,7 @@
|
|||||||
"view_all": "Προβολή Όλων"
|
"view_all": "Προβολή Όλων"
|
||||||
},
|
},
|
||||||
"recent_estimate_card": {
|
"recent_estimate_card": {
|
||||||
"title": "Πρόσφατες Εκτιμήσεις",
|
"title": "Πρόσφατες προσφορές",
|
||||||
"date": "Ημερομηνία",
|
"date": "Ημερομηνία",
|
||||||
"customer": "Πελάτης",
|
"customer": "Πελάτης",
|
||||||
"amount_due": "Οφειλόμενο Ποσό",
|
"amount_due": "Οφειλόμενο Ποσό",
|
||||||
@ -264,18 +264,18 @@
|
|||||||
"deleted_message": "Ο υπολογισμός διαγράφηκε επιτυχώς"
|
"deleted_message": "Ο υπολογισμός διαγράφηκε επιτυχώς"
|
||||||
},
|
},
|
||||||
"estimates": {
|
"estimates": {
|
||||||
"title": "Εκτιμήσεις",
|
"title": "Προσφορές",
|
||||||
"accept_estimate": "Accept Estimate",
|
"accept_estimate": "Accept Estimate",
|
||||||
"reject_estimate": "Reject Estimate",
|
"reject_estimate": "Reject Estimate",
|
||||||
"estimate": "Εκτίμηση | Εκτιμήσεις",
|
"estimate": "Προσφορά | Προσφορές",
|
||||||
"estimates_list": "Λίστα Εκτιμήσεων",
|
"estimates_list": "Λίστα προσφορών",
|
||||||
"days": "{days} Ημέρες",
|
"days": "{days} Ημέρες",
|
||||||
"months": "{months} Μήνας",
|
"months": "{months} Μήνας",
|
||||||
"years": "{years} Έτος",
|
"years": "{years} Έτος",
|
||||||
"all": "Όλα",
|
"all": "Όλα",
|
||||||
"paid": "Εξοφλημένο",
|
"paid": "Εξοφλημένο",
|
||||||
"unpaid": "Ανεξόφλητο",
|
"unpaid": "Ανεξόφλητο",
|
||||||
"customer": "ΤΕΛΩΝΕΙΑΚΗ",
|
"customer": "Πελάτης",
|
||||||
"ref_no": "REF NO.",
|
"ref_no": "REF NO.",
|
||||||
"number": "ΑΡΙΘΜΟΣ",
|
"number": "ΑΡΙΘΜΟΣ",
|
||||||
"amount_due": "ΠΟΣΟ ΠΡΟΣ ΠΛΗΡΩΜΗ",
|
"amount_due": "ΠΟΣΟ ΠΡΟΣ ΠΛΗΡΩΜΗ",
|
||||||
@ -300,7 +300,7 @@
|
|||||||
"convert_to_invoice": "Μετατράπηκε σε Τιμολόγιο",
|
"convert_to_invoice": "Μετατράπηκε σε Τιμολόγιο",
|
||||||
"mark_as_sent": "Σήμανση ως απεσταλμένου",
|
"mark_as_sent": "Σήμανση ως απεσταλμένου",
|
||||||
"send_estimate": "Νέα Εκτίμηση",
|
"send_estimate": "Νέα Εκτίμηση",
|
||||||
"resend_estimate": "Πρόσφατες Εκτιμήσεις",
|
"resend_estimate": "Πρόσφατες προσφορές",
|
||||||
"record_payment": "Καταγραφή Πληρωμής",
|
"record_payment": "Καταγραφή Πληρωμής",
|
||||||
"add_estimate": "Νέα Εκτίμηση",
|
"add_estimate": "Νέα Εκτίμηση",
|
||||||
"save_estimate": "Νέα Εκτίμηση",
|
"save_estimate": "Νέα Εκτίμηση",
|
||||||
@ -310,7 +310,7 @@
|
|||||||
"confirm_mark_as_sent": "Η εκτίμηση αυτή θα επισημανθεί ως εστάλη",
|
"confirm_mark_as_sent": "Η εκτίμηση αυτή θα επισημανθεί ως εστάλη",
|
||||||
"confirm_mark_as_accepted": "Αυτό το τιμολόγιο θα επισημανθεί ως Απορριπτόμενο",
|
"confirm_mark_as_accepted": "Αυτό το τιμολόγιο θα επισημανθεί ως Απορριπτόμενο",
|
||||||
"confirm_mark_as_rejected": "Αυτό το τιμολόγιο θα επισημανθεί ως Απορριπτόμενο",
|
"confirm_mark_as_rejected": "Αυτό το τιμολόγιο θα επισημανθεί ως Απορριπτόμενο",
|
||||||
"no_matching_estimates": "Δεν υπάρχουν αντίστοιχες εκτιμήσεις!",
|
"no_matching_estimates": "Δεν υπάρχουν αντίστοιχες προσφορές!",
|
||||||
"mark_as_sent_successfully": "Το τιμολόγιο επισημάνθηκε ως απεσταλμένο επιτυχώς",
|
"mark_as_sent_successfully": "Το τιμολόγιο επισημάνθηκε ως απεσταλμένο επιτυχώς",
|
||||||
"send_estimate_successfully": "Το τιμολόγιο εστάλη επιτυχώς",
|
"send_estimate_successfully": "Το τιμολόγιο εστάλη επιτυχώς",
|
||||||
"errors": {
|
"errors": {
|
||||||
@ -328,9 +328,9 @@
|
|||||||
"update_Estimate": "Ενημέρωση εκτίμησης",
|
"update_Estimate": "Ενημέρωση εκτίμησης",
|
||||||
"edit_estimate": "Επεξεργασία Εκτίμησης",
|
"edit_estimate": "Επεξεργασία Εκτίμησης",
|
||||||
"items": "στοιχεία",
|
"items": "στοιχεία",
|
||||||
"Estimate": "Εκτίμηση | Εκτιμήσεις",
|
"Estimate": "Προσφορά | Προσφορές",
|
||||||
"add_new_tax": "Προσθήκη Νέου Φόρου",
|
"add_new_tax": "Προσθήκη Νέου Φόρου",
|
||||||
"no_estimates": "Δεν υπάρχουν εκτιμήσεις ακόμα!",
|
"no_estimates": "Δεν υπάρχουν προσφορές ακόμα!",
|
||||||
"list_of_estimates": "Αυτή η ενότητα θα περιέχει τη λίστα των στοιχείων.",
|
"list_of_estimates": "Αυτή η ενότητα θα περιέχει τη λίστα των στοιχείων.",
|
||||||
"mark_as_rejected": "Σήμανση ως απορρίφθηκε",
|
"mark_as_rejected": "Σήμανση ως απορρίφθηκε",
|
||||||
"mark_as_accepted": "Σήμανση ως αποδεκτό",
|
"mark_as_accepted": "Σήμανση ως αποδεκτό",
|
||||||
@ -372,7 +372,7 @@
|
|||||||
"viewed": "Προβλήθηκαν",
|
"viewed": "Προβλήθηκαν",
|
||||||
"overdue": "Εκπρόθεσμα",
|
"overdue": "Εκπρόθεσμα",
|
||||||
"completed": "Ολοκληρώθηκε",
|
"completed": "Ολοκληρώθηκε",
|
||||||
"customer": "ΤΕΛΩΝΕΙΑΚΗ",
|
"customer": "Πελάτης",
|
||||||
"paid_status": "ΚΑΤΑΣΤΑΣΗ ΠΛΗΡΩΜΗΣ",
|
"paid_status": "ΚΑΤΑΣΤΑΣΗ ΠΛΗΡΩΜΗΣ",
|
||||||
"ref_no": "REF NO.",
|
"ref_no": "REF NO.",
|
||||||
"number": "ΑΡΙΘΜΟΣ",
|
"number": "ΑΡΙΘΜΟΣ",
|
||||||
@ -462,7 +462,7 @@
|
|||||||
"overdue": "Εκπρόθεσμα",
|
"overdue": "Εκπρόθεσμα",
|
||||||
"active": "Ενεργή",
|
"active": "Ενεργή",
|
||||||
"completed": "Ολοκληρώθηκε",
|
"completed": "Ολοκληρώθηκε",
|
||||||
"customer": "ΤΕΛΩΝΕΙΑΚΗ",
|
"customer": "Πελάτης",
|
||||||
"paid_status": "ΚΑΤΑΣΤΑΣΗ ΠΛΗΡΩΜΗΣ",
|
"paid_status": "ΚΑΤΑΣΤΑΣΗ ΠΛΗΡΩΜΗΣ",
|
||||||
"ref_no": "REF NO.",
|
"ref_no": "REF NO.",
|
||||||
"number": "ΑΡΙΘΜΟΣ",
|
"number": "ΑΡΙΘΜΟΣ",
|
||||||
@ -681,7 +681,7 @@
|
|||||||
"module_enabled": "Module Enabled",
|
"module_enabled": "Module Enabled",
|
||||||
"update_to": "Update To",
|
"update_to": "Update To",
|
||||||
"module_updated": "Module Updated Successfully!",
|
"module_updated": "Module Updated Successfully!",
|
||||||
"title": "Modules",
|
"title": "Πρόσθετα",
|
||||||
"module": "Module | Modules",
|
"module": "Module | Modules",
|
||||||
"api_token": "API token",
|
"api_token": "API token",
|
||||||
"invalid_api_token": "Invalid API Token.",
|
"invalid_api_token": "Invalid API Token.",
|
||||||
@ -692,13 +692,13 @@
|
|||||||
"module_not_found": "Module Not Found",
|
"module_not_found": "Module Not Found",
|
||||||
"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": "Last Updated On",
|
||||||
"connect_installation": "Connect your installation",
|
"connect_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": "Συνδεθείτε στο {url} και συνδέστε αυτήν την εγκατάσταση εισάγοντας το API Token. Τα πρόσθετα που αγοράσατε θα εμφανιστούν εδώ μετά την ολοκλήρωση της σύνδεσης.",
|
||||||
"view_module": "View Module",
|
"view_module": "Δείτε το πρόσθετο",
|
||||||
"update_available": "Update Available",
|
"update_available": "Διαθέσιμη ανανέωση",
|
||||||
"purchased": "Purchased",
|
"purchased": "Purchased",
|
||||||
"installed": "Installed",
|
"installed": "Εγκαταστάθηκε",
|
||||||
"no_modules_installed": "No Modules Installed Yet!",
|
"no_modules_installed": "Δεν υπάρχουν ακόμα εγκατεστημένα πρόσθετα!",
|
||||||
"disable_warning": "All the settings for this particular will be reverted.",
|
"disable_warning": "All the settings for this particular will be reverted.",
|
||||||
"what_you_get": "What you get"
|
"what_you_get": "What you get"
|
||||||
},
|
},
|
||||||
@ -815,7 +815,7 @@
|
|||||||
"title": "Ρυθμίσεις",
|
"title": "Ρυθμίσεις",
|
||||||
"setting": "Ρύθμιση Ρυθμίσεων",
|
"setting": "Ρύθμιση Ρυθμίσεων",
|
||||||
"general": "General",
|
"general": "General",
|
||||||
"language": "Language",
|
"language": "Γλώσσα",
|
||||||
"primary_currency": "Κύριο Νόμισμα",
|
"primary_currency": "Κύριο Νόμισμα",
|
||||||
"timezone": "Ζώνη Ώρας",
|
"timezone": "Ζώνη Ώρας",
|
||||||
"date_format": "Μορφή Ημερομηνίας",
|
"date_format": "Μορφή Ημερομηνίας",
|
||||||
|
|||||||
@ -100,7 +100,9 @@
|
|||||||
"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",
|
||||||
@ -233,7 +235,8 @@
|
|||||||
"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",
|
||||||
@ -728,7 +731,8 @@
|
|||||||
"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",
|
||||||
@ -807,7 +811,8 @@
|
|||||||
"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."
|
||||||
@ -1311,6 +1316,51 @@
|
|||||||
"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": {
|
||||||
@ -1485,7 +1535,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",
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
"settings": "Ajustes",
|
"settings": "Ajustes",
|
||||||
"logout": "Cerrar sesión",
|
"logout": "Cerrar sesión",
|
||||||
"users": "Usuarios",
|
"users": "Usuarios",
|
||||||
"modules": "Modules"
|
"modules": "Módulos"
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"add_company": "Añadir empresa",
|
"add_company": "Añadir empresa",
|
||||||
@ -49,7 +49,7 @@
|
|||||||
"view": "Ver",
|
"view": "Ver",
|
||||||
"add_new_item": "Agregar ítem nuevo",
|
"add_new_item": "Agregar ítem nuevo",
|
||||||
"clear_all": "Limpiar todo",
|
"clear_all": "Limpiar todo",
|
||||||
"showing": "Mostrando",
|
"showing": "Mostrar",
|
||||||
"of": "de",
|
"of": "de",
|
||||||
"actions": "Acciones",
|
"actions": "Acciones",
|
||||||
"subtotal": "SUBTOTAL",
|
"subtotal": "SUBTOTAL",
|
||||||
@ -93,14 +93,14 @@
|
|||||||
"no_note_found": "No se encontró ninguna nota",
|
"no_note_found": "No se encontró ninguna nota",
|
||||||
"insert_note": "Insertar una nota",
|
"insert_note": "Insertar una nota",
|
||||||
"copied_pdf_url_clipboard": "Copiar Url al portapapeles",
|
"copied_pdf_url_clipboard": "Copiar Url al portapapeles",
|
||||||
"copied_url_clipboard": "Copied url to clipboard!",
|
"copied_url_clipboard": "¡URL copiada al portapapeles!",
|
||||||
"docs": "Documentación",
|
"docs": "Documentación",
|
||||||
"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": "Logeado Satisfactoriamente!",
|
||||||
"logged_out_successfully": "Logeado Satisfactoriamente",
|
"logged_out_successfully": "Logeado Satisfactoriamente",
|
||||||
"mark_as_default": "Mark as default"
|
"mark_as_default": "Marcar como predeterminado"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"select_year": "Seleccionar año",
|
"select_year": "Seleccionar año",
|
||||||
@ -109,7 +109,7 @@
|
|||||||
"customers": "Clientes",
|
"customers": "Clientes",
|
||||||
"invoices": "Facturas",
|
"invoices": "Facturas",
|
||||||
"estimates": "Presupuestos",
|
"estimates": "Presupuestos",
|
||||||
"payments": "Payments"
|
"payments": "Ver Medios de Pago"
|
||||||
},
|
},
|
||||||
"chart_info": {
|
"chart_info": {
|
||||||
"total_sales": "Ventas",
|
"total_sales": "Ventas",
|
||||||
@ -208,10 +208,10 @@
|
|||||||
"new_customer": "Nuevo cliente",
|
"new_customer": "Nuevo cliente",
|
||||||
"edit_customer": "Editar cliente",
|
"edit_customer": "Editar cliente",
|
||||||
"basic_info": "Información básica",
|
"basic_info": "Información básica",
|
||||||
"portal_access": "Portal Access",
|
"portal_access": "Acceso al portal",
|
||||||
"portal_access_text": "Would you like to allow this customer to login to the Customer Portal?",
|
"portal_access_text": "¿Le gustaría permitir que este cliente inicie sesión en el Portal del Cliente?",
|
||||||
"portal_access_url": "Portal URL del cliente",
|
"portal_access_url": "Portal URL del cliente",
|
||||||
"portal_access_url_help": "Please copy & forward the above given URL to your customer for providing access.",
|
"portal_access_url_help": "Por favor, copie y reenvíe la URL anterior a su cliente para proporcionar acceso.",
|
||||||
"billing_address": "Dirección de Facturación",
|
"billing_address": "Dirección de Facturación",
|
||||||
"shipping_address": "Dirección de Envío",
|
"shipping_address": "Dirección de Envío",
|
||||||
"copy_billing_address": "Copia de facturación",
|
"copy_billing_address": "Copia de facturación",
|
||||||
@ -231,7 +231,7 @@
|
|||||||
"confirm_delete": "No podrá recuperar este cliente y todas las facturas, estimaciones y pagos relacionados. | No podrá recuperar estos clientes y todas las facturas, estimaciones y pagos relacionados.",
|
"confirm_delete": "No podrá recuperar este cliente y todas las facturas, estimaciones y pagos relacionados. | No podrá recuperar estos clientes y todas las facturas, estimaciones y pagos relacionados.",
|
||||||
"created_message": "Cliente creado con éxito",
|
"created_message": "Cliente creado con éxito",
|
||||||
"updated_message": "Cliente actualizado con éxito",
|
"updated_message": "Cliente actualizado con éxito",
|
||||||
"address_updated_message": "Address Information Updated succesfully",
|
"address_updated_message": "Información del domicilio actualizado correctamente",
|
||||||
"deleted_message": "Cliente eliminado correctamente | Clientes eliminados exitosamente",
|
"deleted_message": "Cliente eliminado correctamente | Clientes eliminados exitosamente",
|
||||||
"edit_currency_not_allowed": "No se puede cambiar la divisa una vez creadas las transacciones."
|
"edit_currency_not_allowed": "No se puede cambiar la divisa una vez creadas las transacciones."
|
||||||
},
|
},
|
||||||
@ -265,8 +265,8 @@
|
|||||||
},
|
},
|
||||||
"estimates": {
|
"estimates": {
|
||||||
"title": "Presupuestos",
|
"title": "Presupuestos",
|
||||||
"accept_estimate": "Accept Estimate",
|
"accept_estimate": "Aceptar cotización",
|
||||||
"reject_estimate": "Reject Estimate",
|
"reject_estimate": "Rechazar cotización",
|
||||||
"estimate": "Presupuesto | Presupuestos",
|
"estimate": "Presupuesto | Presupuestos",
|
||||||
"estimates_list": "Lista de presupuestos",
|
"estimates_list": "Lista de presupuestos",
|
||||||
"days": "{días} Días",
|
"days": "{días} Días",
|
||||||
@ -318,10 +318,10 @@
|
|||||||
},
|
},
|
||||||
"accepted": "Aceptado",
|
"accepted": "Aceptado",
|
||||||
"rejected": "Rechazado",
|
"rejected": "Rechazado",
|
||||||
"expired": "Expired",
|
"expired": "Caducado",
|
||||||
"sent": "Enviado",
|
"sent": "Enviado",
|
||||||
"draft": "Borrador",
|
"draft": "Borrador",
|
||||||
"viewed": "Viewed",
|
"viewed": "Visto",
|
||||||
"declined": "Rechazado",
|
"declined": "Rechazado",
|
||||||
"new_estimate": "Nuevo presupuesto",
|
"new_estimate": "Nuevo presupuesto",
|
||||||
"add_new_estimate": "Añadir nuevo presupuesto",
|
"add_new_estimate": "Añadir nuevo presupuesto",
|
||||||
@ -355,14 +355,14 @@
|
|||||||
"select_an_item": "Escriba o haga clic para seleccionar un elemento",
|
"select_an_item": "Escriba o haga clic para seleccionar un elemento",
|
||||||
"type_item_description": "Descripción del tipo de elemento(opcional)"
|
"type_item_description": "Descripción del tipo de elemento(opcional)"
|
||||||
},
|
},
|
||||||
"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 se activa, esta plantilla se selccionará automáticamente para nuevos presupuestos. "
|
||||||
},
|
},
|
||||||
"invoices": {
|
"invoices": {
|
||||||
"title": "Facturas",
|
"title": "Facturas",
|
||||||
"download": "Download",
|
"download": "Descargar",
|
||||||
"pay_invoice": "Pay Invoice",
|
"pay_invoice": "Pagar factura",
|
||||||
"invoices_list": "Lista de facturas",
|
"invoices_list": "Lista de facturas",
|
||||||
"invoice_information": "Invoice Information",
|
"invoice_information": "Información de la factura",
|
||||||
"days": "{días} Días",
|
"days": "{días} Días",
|
||||||
"months": "{meses} Mes",
|
"months": "{meses} Mes",
|
||||||
"years": "{años} Año",
|
"years": "{años} Año",
|
||||||
@ -447,7 +447,7 @@
|
|||||||
"marked_as_sent_message": "Factura marcada como enviada con éxito",
|
"marked_as_sent_message": "Factura marcada como enviada con éxito",
|
||||||
"something_went_wrong": "Algo fue mal",
|
"something_went_wrong": "Algo fue mal",
|
||||||
"invalid_due_amount_message": "El pago introducido es mayor que el importe total pendiente de esta factura. Por favor, verificalo y vuelve a intentarlo.",
|
"invalid_due_amount_message": "El pago introducido es mayor que el importe total pendiente de esta factura. Por favor, verificalo y vuelve a intentarlo.",
|
||||||
"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 se activa, esta plantilla se seleccionará automáticamente para nuevas facturas. "
|
||||||
},
|
},
|
||||||
"recurring_invoices": {
|
"recurring_invoices": {
|
||||||
"title": "Facturas recurrentes",
|
"title": "Facturas recurrentes",
|
||||||
@ -526,7 +526,7 @@
|
|||||||
"cloned_successfully": "Factura recurrente clonada con éxito",
|
"cloned_successfully": "Factura recurrente clonada con éxito",
|
||||||
"clone_invoice": "Clonar factura recurrente",
|
"clone_invoice": "Clonar factura recurrente",
|
||||||
"confirm_clone": "Esta factura recurrente será clonada en una nueva factura recurrente",
|
"confirm_clone": "Esta factura recurrente será clonada en una nueva factura recurrente",
|
||||||
"add_customer_email": "Please add an email address for this customer to send invoices automatically.",
|
"add_customer_email": "Por favor, agregue una dirección de correo electrónico para que este cliente envíe las facturas automáticamente.",
|
||||||
"item": {
|
"item": {
|
||||||
"title": "Título del artículo",
|
"title": "Título del artículo",
|
||||||
"description": "Descripción",
|
"description": "Descripción",
|
||||||
@ -658,49 +658,49 @@
|
|||||||
"retype_password": "Reescriba la contraseña"
|
"retype_password": "Reescriba la contraseña"
|
||||||
},
|
},
|
||||||
"modules": {
|
"modules": {
|
||||||
"buy_now": "Buy Now",
|
"buy_now": "Comprar ahora",
|
||||||
"install": "Install",
|
"install": "Instalar",
|
||||||
"price": "Price",
|
"price": "Precio",
|
||||||
"download_zip_file": "Download ZIP file",
|
"download_zip_file": "Descargar archivo ZIP",
|
||||||
"unzipping_package": "Unzipping Package",
|
"unzipping_package": "Descomprimir paquete",
|
||||||
"copying_files": "Copying Files",
|
"copying_files": "Copiando archivos",
|
||||||
"deleting_files": "Deleting Unused files",
|
"deleting_files": "Eliminando archivos no usados",
|
||||||
"completing_installation": "Completing Installation",
|
"completing_installation": "Completando la instalación",
|
||||||
"update_failed": "Update Failed",
|
"update_failed": "Falló la actualización",
|
||||||
"install_success": "Module has been installed successfully!",
|
"install_success": "¡El módulo se ha instalado correctamente!",
|
||||||
"customer_reviews": "Reviews",
|
"customer_reviews": "Reseñas",
|
||||||
"license": "License",
|
"license": "Licencia",
|
||||||
"faq": "FAQ",
|
"faq": "Preguntas Frecuentes (FAQ)",
|
||||||
"monthly": "Monthly",
|
"monthly": "Mensual",
|
||||||
"yearly": "Yearly",
|
"yearly": "Anual",
|
||||||
"updated": "Updated",
|
"updated": "Actualizado",
|
||||||
"version": "Version",
|
"version": "Versión",
|
||||||
"disable": "Disable",
|
"disable": "Deshabilitar",
|
||||||
"module_disabled": "Module Disabled",
|
"module_disabled": "Módulo desactivado",
|
||||||
"enable": "Enable",
|
"enable": "Habilitar",
|
||||||
"module_enabled": "Module Enabled",
|
"module_enabled": "Módulo habilitado",
|
||||||
"update_to": "Update To",
|
"update_to": "Actualizar a",
|
||||||
"module_updated": "Module Updated Successfully!",
|
"module_updated": "¡Módulo actualizado correctamente!",
|
||||||
"title": "Modules",
|
"title": "Módulos",
|
||||||
"module": "Module | Modules",
|
"module": "Módulo | Módulos",
|
||||||
"api_token": "API token",
|
"api_token": "API token",
|
||||||
"invalid_api_token": "Invalid API Token.",
|
"invalid_api_token": "API Token inválido.",
|
||||||
"other_modules": "Other Modules",
|
"other_modules": "Otros módulos",
|
||||||
"view_all": "View All",
|
"view_all": "Ver todo",
|
||||||
"no_reviews_found": "There are no reviews for this module yet!",
|
"no_reviews_found": "¡Este módulo aún no tiene reseñas!",
|
||||||
"module_not_purchased": "Module Not Purchased",
|
"module_not_purchased": "Módulo no comprado",
|
||||||
"module_not_found": "Module Not Found",
|
"module_not_found": "Módulo no encontrado",
|
||||||
"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": "Actualizado",
|
||||||
"connect_installation": "Connect your installation",
|
"connect_installation": "Conecte su instalación",
|
||||||
"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": "Inicie sesión en {url} y conecte esta instalación introduciendo el token de API. Los módulos comprados aparecerán aquí después de establecer la conexión.",
|
||||||
"view_module": "View Module",
|
"view_module": "Ver módulo",
|
||||||
"update_available": "Update Available",
|
"update_available": "Actualización disponible",
|
||||||
"purchased": "Purchased",
|
"purchased": "Comprado",
|
||||||
"installed": "Installed",
|
"installed": "Instalado",
|
||||||
"no_modules_installed": "No Modules Installed Yet!",
|
"no_modules_installed": "¡No hay módulos instalados todavía!",
|
||||||
"disable_warning": "All the settings for this particular will be reverted.",
|
"disable_warning": "Se revertirán todos los ajustes para este particular.",
|
||||||
"what_you_get": "What you get"
|
"what_you_get": "Beneficios que obtiene"
|
||||||
},
|
},
|
||||||
"users": {
|
"users": {
|
||||||
"title": "Usuarios",
|
"title": "Usuarios",
|
||||||
@ -807,10 +807,10 @@
|
|||||||
"payment_modes": "Formas de pago",
|
"payment_modes": "Formas de pago",
|
||||||
"notes": "Notas",
|
"notes": "Notas",
|
||||||
"exchange_rate": "Tasa de cambio",
|
"exchange_rate": "Tasa de cambio",
|
||||||
"address_information": "Address Information"
|
"address_information": "Información de dirección"
|
||||||
},
|
},
|
||||||
"address_information": {
|
"address_information": {
|
||||||
"section_description": " You can update Your Address information using form below."
|
"section_description": "Puede actualizar la información de su dirección utilizando el siguiente formulario."
|
||||||
},
|
},
|
||||||
"title": "Configuraciones",
|
"title": "Configuraciones",
|
||||||
"setting": "Configuraciones | Configuraciones",
|
"setting": "Configuraciones | Configuraciones",
|
||||||
@ -1112,9 +1112,9 @@
|
|||||||
"error": " No puede eliminar el controlador activo",
|
"error": " No puede eliminar el controlador activo",
|
||||||
"default_currency_error": "Esta moneda ya se usa en uno de los proveedores activos",
|
"default_currency_error": "Esta moneda ya se usa en uno de los proveedores activos",
|
||||||
"exchange_help_text": "Ingrese el tipo de cambio para convertir de {currency} a {baseCurrency}",
|
"exchange_help_text": "Ingrese el tipo de cambio para convertir de {currency} a {baseCurrency}",
|
||||||
"currency_freak": "Currency Freak",
|
"currency_freak": "Moneda",
|
||||||
"currency_layer": "Currency Layer",
|
"currency_layer": "Capa de moneda",
|
||||||
"open_exchange_rate": "Open Exchange Rate",
|
"open_exchange_rate": "Tasa de cambio",
|
||||||
"currency_converter": "Conversor de moneda",
|
"currency_converter": "Conversor de moneda",
|
||||||
"server": "Servidor",
|
"server": "Servidor",
|
||||||
"url": "URL",
|
"url": "URL",
|
||||||
@ -1150,8 +1150,8 @@
|
|||||||
"payment_mode_added": "Forma de pago añadida",
|
"payment_mode_added": "Forma de pago añadida",
|
||||||
"payment_mode_updated": "Forma de pago actualizada",
|
"payment_mode_updated": "Forma de pago actualizada",
|
||||||
"payment_mode_confirm_delete": "No podrás recuperar este Modo de Pago",
|
"payment_mode_confirm_delete": "No podrás recuperar este Modo de Pago",
|
||||||
"payments_attached": "This payment method is already attached to payments. Please delete the attached payments to proceed with deletion.",
|
"payments_attached": "Esta forma de pago ya está vinculada a los pagos. Por favor, elimine los pagos adjuntos para proceder con la eliminación.",
|
||||||
"expenses_attached": "This payment method is already attached to expenses. Please delete the attached expenses to proceed with deletion.",
|
"expenses_attached": "Esta forma de pago ya está adjunta a los gastos. Por favor, elimine los gastos adjuntos para proceder con la eliminación.",
|
||||||
"deleted_message": "Método de pago eliminado correctamente"
|
"deleted_message": "Método de pago eliminado correctamente"
|
||||||
},
|
},
|
||||||
"expense_category": {
|
"expense_category": {
|
||||||
@ -1178,8 +1178,8 @@
|
|||||||
"discount_setting": "Ajuste de descuento",
|
"discount_setting": "Ajuste de descuento",
|
||||||
"discount_per_item": "Descuento por artículo",
|
"discount_per_item": "Descuento por artículo",
|
||||||
"discount_setting_description": "Habilítelo si desea agregar Descuento a artículos de factura individuales. Por defecto, los descuentos se agregan directamente a la factura.",
|
"discount_setting_description": "Habilítelo si desea agregar Descuento a artículos de factura individuales. Por defecto, los descuentos se agregan directamente a la factura.",
|
||||||
"expire_public_links": "Automatically Expire Public Links",
|
"expire_public_links": "Expirar automáticamente enlaces públicos",
|
||||||
"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": "Especifique si desea expirar todos los enlaces enviados por la aplicación para ver facturas, estimaciones y pagos, etc. después de una duración especificada.",
|
||||||
"save": "Guardar",
|
"save": "Guardar",
|
||||||
"preference": "Preferencia | Preferencias",
|
"preference": "Preferencia | Preferencias",
|
||||||
"general_settings": "Preferencias predeterminadas para el sistema.",
|
"general_settings": "Preferencias predeterminadas para el sistema.",
|
||||||
@ -1301,16 +1301,16 @@
|
|||||||
"invalid_disk_credentials": "Credencial no válida del disco seleccionado"
|
"invalid_disk_credentials": "Credencial no válida del disco seleccionado"
|
||||||
},
|
},
|
||||||
"taxations": {
|
"taxations": {
|
||||||
"add_billing_address": "Enter Billing Address",
|
"add_billing_address": "Introduzca su dirección de facturación",
|
||||||
"add_shipping_address": "Enter Shipping Address",
|
"add_shipping_address": "Introduzca la dirección de envío",
|
||||||
"add_company_address": "Enter Company Address",
|
"add_company_address": "Introduzca la dirección de la empresa",
|
||||||
"modal_description": "The information below is required in order to fetch sales tax.",
|
"modal_description": "La siguiente información es requerida para obtener el impuesto de venta.",
|
||||||
"add_address": "Add Address for fetching sales tax.",
|
"add_address": "Añadir dirección para obtener impuestos de venta.",
|
||||||
"address_placeholder": "Example: 123, My Street",
|
"address_placeholder": "Ejemplo: 123, Mi Calle",
|
||||||
"city_placeholder": "Example: Los Angeles",
|
"city_placeholder": "Ejemplo: Los Angeles",
|
||||||
"state_placeholder": "Example: CA",
|
"state_placeholder": "Ejemplo: CA",
|
||||||
"zip_placeholder": "Example: 90024",
|
"zip_placeholder": "Ejemplo: 90024",
|
||||||
"invalid_address": "Please provide valid address details."
|
"invalid_address": "Proporciona una dirección válida."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"wizard": {
|
"wizard": {
|
||||||
@ -1470,17 +1470,17 @@
|
|||||||
"not_allowed": "No permitido",
|
"not_allowed": "No permitido",
|
||||||
"login_invalid_credentials": "Estas credenciales no coinciden con nuestros registros.",
|
"login_invalid_credentials": "Estas credenciales no coinciden con nuestros registros.",
|
||||||
"enter_valid_cron_format": "Por favor, introduzca un formato cron válido",
|
"enter_valid_cron_format": "Por favor, introduzca un formato cron válido",
|
||||||
"email_could_not_be_sent": "Email could not be sent to this email address.",
|
"email_could_not_be_sent": "No se pudo enviar el correo a esta dirección de correo electrónico.",
|
||||||
"invalid_address": "Please enter a valid address.",
|
"invalid_address": "Por favor, introduzca una dirección válida.",
|
||||||
"invalid_key": "Please enter valid key.",
|
"invalid_key": "Por favor, introduzca una clave válida.",
|
||||||
"invalid_state": "Please enter a valid state.",
|
"invalid_state": "Por favor, introduzca un estado válido.",
|
||||||
"invalid_city": "Please enter a valid city.",
|
"invalid_city": "Por favor, introduzca una ciudad válida.",
|
||||||
"invalid_postal_code": "Please enter a valid zip.",
|
"invalid_postal_code": "Por favor, introduzca un código postal válido.",
|
||||||
"invalid_format": "Please enter valid query string format.",
|
"invalid_format": "Por favor, introduzca un formato de consulta válido.",
|
||||||
"api_error": "Server not responding.",
|
"api_error": "El servidor no responde.",
|
||||||
"feature_not_enabled": "Feature not enabled.",
|
"feature_not_enabled": "Característica no habilitada.",
|
||||||
"request_limit_met": "Api request limit exceeded.",
|
"request_limit_met": "Ha alcanzado el límite de solicitudes.",
|
||||||
"address_incomplete": "Incomplete Address"
|
"address_incomplete": "Dirección incompleta"
|
||||||
},
|
},
|
||||||
"pdf_estimate_label": "Presupuestar",
|
"pdf_estimate_label": "Presupuestar",
|
||||||
"pdf_estimate_number": "Número de Presupuesto",
|
"pdf_estimate_number": "Número de Presupuesto",
|
||||||
|
|||||||
@ -77,7 +77,7 @@
|
|||||||
"list_is_empty": "La liste est vide.",
|
"list_is_empty": "La liste est vide.",
|
||||||
"no_tax_found": "Aucune taxe trouvée !",
|
"no_tax_found": "Aucune taxe trouvée !",
|
||||||
"four_zero_four": "404",
|
"four_zero_four": "404",
|
||||||
"you_got_lost": "Oups ! Vous vous êtes perdus !",
|
"you_got_lost": "Oups! Vous vous êtes perdus!",
|
||||||
"go_home": "Retour au tableau de bord",
|
"go_home": "Retour au tableau de bord",
|
||||||
"test_mail_conf": "Envoyer un email de test",
|
"test_mail_conf": "Envoyer un email de test",
|
||||||
"send_mail_successfully": "Email envoyé",
|
"send_mail_successfully": "Email envoyé",
|
||||||
@ -93,14 +93,14 @@
|
|||||||
"no_note_found": "Aucune note de bas de page trouvée",
|
"no_note_found": "Aucune note de bas de page trouvée",
|
||||||
"insert_note": "Insérer une note",
|
"insert_note": "Insérer une note",
|
||||||
"copied_pdf_url_clipboard": "L'adresse du PDF a été copiée.",
|
"copied_pdf_url_clipboard": "L'adresse du PDF a été copiée.",
|
||||||
"copied_url_clipboard": "URL copiée vers le presse-papier !",
|
"copied_url_clipboard": "URL copiée vers le presse-papier!",
|
||||||
"docs": "Documents",
|
"docs": "Documents",
|
||||||
"do_you_wish_to_continue": "Voulez-vous continuer ?",
|
"do_you_wish_to_continue": "Voulez-vous continuer ?",
|
||||||
"note": "Note de bas de page",
|
"note": "Note de bas de page",
|
||||||
"pay_invoice": "Payer facture",
|
"pay_invoice": "Payer facture",
|
||||||
"login_successfully": "Identifié avec succès !",
|
"login_successfully": "Identifié avec succès!",
|
||||||
"logged_out_successfully": "Déconnecté avec succès",
|
"logged_out_successfully": "Déconnecté avec succès",
|
||||||
"mark_as_default": "Définir par défaut"
|
"mark_as_default": "Marquer par défaut"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"select_year": "Sélectionnez l'année",
|
"select_year": "Sélectionnez l'année",
|
||||||
@ -211,7 +211,7 @@
|
|||||||
"portal_access": "Accès Portail",
|
"portal_access": "Accès Portail",
|
||||||
"portal_access_text": "Souhaitez vous autoriser ce client à se connecter au Portail Client ?",
|
"portal_access_text": "Souhaitez vous autoriser ce client à se connecter au Portail Client ?",
|
||||||
"portal_access_url": "URL de connexion Portail Client",
|
"portal_access_url": "URL de connexion Portail Client",
|
||||||
"portal_access_url_help": "Veuillez copier et envoyer le lien ci-dessus au client pour lui fournir l'accès au portail.",
|
"portal_access_url_help": "Veuillez copiez et envoyez le lien ci-dessus au client pour lui fournir l'accès au portail.",
|
||||||
"billing_address": "Adresse de facturation",
|
"billing_address": "Adresse de facturation",
|
||||||
"shipping_address": "Adresse de livraison",
|
"shipping_address": "Adresse de livraison",
|
||||||
"copy_billing_address": "Copier depuis l'adresse de facturation",
|
"copy_billing_address": "Copier depuis l'adresse de facturation",
|
||||||
@ -321,7 +321,7 @@
|
|||||||
"expired": "Expiré",
|
"expired": "Expiré",
|
||||||
"sent": "Envoyé",
|
"sent": "Envoyé",
|
||||||
"draft": "Brouillon",
|
"draft": "Brouillon",
|
||||||
"viewed": "Vu",
|
"viewed": "Consultée",
|
||||||
"declined": "Refusé",
|
"declined": "Refusé",
|
||||||
"new_estimate": "Nouveau devis",
|
"new_estimate": "Nouveau devis",
|
||||||
"add_new_estimate": "Nouveau devis",
|
"add_new_estimate": "Nouveau devis",
|
||||||
@ -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": "Si activé, le modèle sélectionné sera automatiquement utilisé pour les nouveaux devis."
|
"mark_as_default_estimate_template_description": "If enabled, the selected template will be automatically selected for new estimates."
|
||||||
},
|
},
|
||||||
"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": "Si activé, le modèle sélectionné sera automatiquement utilisé pour les nouvelles factures."
|
"mark_as_default_invoice_template_description": "If enabled, the selected template will be automatically selected for new invoices."
|
||||||
},
|
},
|
||||||
"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": "Merci d'ajouter un email à ce client pour envoyer les factures automatiquement.",
|
"add_customer_email": "Please add an email address for this customer to send invoices automatically.",
|
||||||
"item": {
|
"item": {
|
||||||
"title": "Nom",
|
"title": "Nom",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
@ -660,47 +660,47 @@
|
|||||||
"modules": {
|
"modules": {
|
||||||
"buy_now": "Acheter maintenant",
|
"buy_now": "Acheter maintenant",
|
||||||
"install": "Installer",
|
"install": "Installer",
|
||||||
"price": "Prox",
|
"price": "Prix",
|
||||||
"download_zip_file": "Télécharger le fichier ZIP",
|
"download_zip_file": "Télécharger le fichier ZIP",
|
||||||
"unzipping_package": "Dézip du paquet en cours",
|
"unzipping_package": "Décompresser le fichier",
|
||||||
"copying_files": "Copie des fichiers en cours",
|
"copying_files": "Copie de fichiers en cours",
|
||||||
"deleting_files": "Suppression des fichiers inutilisés",
|
"deleting_files": "Supprimer les fichiers inutilisés",
|
||||||
"completing_installation": "Finalisation de l'installation",
|
"completing_installation": "Terminer l'installation",
|
||||||
"update_failed": "Mise à jour échouée",
|
"update_failed": "Échec de la mise à jour",
|
||||||
"install_success": "Le module a été installé avec succès !",
|
"install_success": "Votre module a été correctement installé !",
|
||||||
"customer_reviews": "Avis",
|
"customer_reviews": "Évaluations",
|
||||||
"license": "Licence",
|
"license": "License",
|
||||||
"faq": "FAQ",
|
"faq": "FAQ",
|
||||||
"monthly": "Mensuel",
|
"monthly": "Mensuel",
|
||||||
"yearly": "Annuel",
|
"yearly": "Annuel",
|
||||||
"updated": "Mise à jour",
|
"updated": "Mis à jour",
|
||||||
"version": "Version",
|
"version": "Version",
|
||||||
"disable": "Désactivé",
|
"disable": "Désactiver",
|
||||||
"module_disabled": "Module désactivé",
|
"module_disabled": "Module désactivé",
|
||||||
"enable": "Activé",
|
"enable": "Activer",
|
||||||
"module_enabled": "Module activé",
|
"module_enabled": "Module activé",
|
||||||
"update_to": "Mettre à jour vers",
|
"update_to": "Mise à jour vers",
|
||||||
"module_updated": "Module mis à jour avec succès !",
|
"module_updated": "Le module a bien été mis à jour !",
|
||||||
"title": "Modules",
|
"title": "Modules",
|
||||||
"module": "Module | Modules",
|
"module": "Module | Modules",
|
||||||
"api_token": "Jeton API",
|
"api_token": "Jeton API",
|
||||||
"invalid_api_token": "Jeton API invalide.",
|
"invalid_api_token": "Jeton API invalide.",
|
||||||
"other_modules": "Autres modules",
|
"other_modules": "Autres modules",
|
||||||
"view_all": "Voir tout",
|
"view_all": "Tout afficher",
|
||||||
"no_reviews_found": "Il n'y a pas encore d'avis pour ce module !",
|
"no_reviews_found": "Il n'y a pas encore d'avis pour ce module !",
|
||||||
"module_not_purchased": "Module non acheté",
|
"module_not_purchased": "Module non acheté",
|
||||||
"module_not_found": "Module introuvable",
|
"module_not_found": "Module non trouvé",
|
||||||
"version_not_supported": "La version de ce module n'est pas supportée par la version en cours de Crater",
|
"version_not_supported": "This module version doesn't support the current version of Crater",
|
||||||
"last_updated": "Dernière mise à jour le",
|
"last_updated": "Mis à jour le",
|
||||||
"connect_installation": "Connecter votre installation",
|
"connect_installation": "Connectez votre installation",
|
||||||
"api_token_description": "Authentifiez-vous sur {url} et connectez votre installation en entrant votre jeton API. Vos modules achetés apparaîtront ici une fois la connection établie.",
|
"api_token_description": "Rendez-vous à {url} et connectez votre application en entrant le jeton d'API. Vos modules achetés apparaîtront ici une fois la connexion établie.",
|
||||||
"view_module": "Voir le module",
|
"view_module": "Afficher le module",
|
||||||
"update_available": "Mise à jour disponible",
|
"update_available": "Mise à jour disponible",
|
||||||
"purchased": "Acheté",
|
"purchased": "Acheté",
|
||||||
"installed": "Installé",
|
"installed": "Installé",
|
||||||
"no_modules_installed": "Aucun module actuellement installé !",
|
"no_modules_installed": "Aucun module installé !",
|
||||||
"disable_warning": "Tous les paramètres pour celui-ci seront annulés.",
|
"disable_warning": "Tous les paramètres de ce module seront réinitialisés.",
|
||||||
"what_you_get": "Ce que vous avez"
|
"what_you_get": "Ce que vous obtenez"
|
||||||
},
|
},
|
||||||
"users": {
|
"users": {
|
||||||
"title": "Utilisateurs",
|
"title": "Utilisateurs",
|
||||||
@ -807,7 +807,7 @@
|
|||||||
"payment_modes": "Modes de paiement",
|
"payment_modes": "Modes de paiement",
|
||||||
"notes": "Notes de bas de page",
|
"notes": "Notes de bas de page",
|
||||||
"exchange_rate": "Taux de change",
|
"exchange_rate": "Taux de change",
|
||||||
"address_information": "Informations d'adresse"
|
"address_information": "Information d'adresse"
|
||||||
},
|
},
|
||||||
"address_information": {
|
"address_information": {
|
||||||
"section_description": " Vous pouvez mettre à jour vos informations d'adresse via le formulaire ci dessous."
|
"section_description": " Vous pouvez mettre à jour vos informations d'adresse via le formulaire ci dessous."
|
||||||
@ -842,7 +842,7 @@
|
|||||||
"port": "Port",
|
"port": "Port",
|
||||||
"driver": "Fournisseur",
|
"driver": "Fournisseur",
|
||||||
"secret": "Secret",
|
"secret": "Secret",
|
||||||
"mailgun_secret": "Mailgun Secret",
|
"mailgun_secret": "Secret Mailgun",
|
||||||
"mailgun_domain": "Domaine",
|
"mailgun_domain": "Domaine",
|
||||||
"mailgun_endpoint": "Mailgun Endpoint",
|
"mailgun_endpoint": "Mailgun Endpoint",
|
||||||
"ses_secret": "SES Secret",
|
"ses_secret": "SES Secret",
|
||||||
@ -922,7 +922,7 @@
|
|||||||
},
|
},
|
||||||
"customization": {
|
"customization": {
|
||||||
"customization": "Personnalisation",
|
"customization": "Personnalisation",
|
||||||
"updated_message": "Informations de la société mises à jour",
|
"updated_message": "Informations la société mises à jour",
|
||||||
"save": "Enregistrer",
|
"save": "Enregistrer",
|
||||||
"insert_fields": "Insérer des champs",
|
"insert_fields": "Insérer des champs",
|
||||||
"learn_custom_format": "Apprenez à utiliser le format personnalisé",
|
"learn_custom_format": "Apprenez à utiliser le format personnalisé",
|
||||||
@ -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": "Cette méthode de paiement est déjà utilisée pour les paiements. Merci de supprimer les paiements associés pour finaliser la suppression.",
|
"payments_attached": "This payment method is already attached to payments. Please delete the attached payments to proceed with deletion.",
|
||||||
"expenses_attached": "Cette méthode de paiement est déjà utilisée pour les dépenses. Merci de supprimer les dépenses associées pour finaliser la suppression.",
|
"expenses_attached": "This payment method is already attached to expenses. Please delete the attached expenses to proceed with deletion.",
|
||||||
"deleted_message": "Mode de paiement supprimé"
|
"deleted_message": "Mode de paiement supprimé"
|
||||||
},
|
},
|
||||||
"expense_category": {
|
"expense_category": {
|
||||||
@ -1210,9 +1210,9 @@
|
|||||||
"latest_message": "Bravo, vous êtes à jour.",
|
"latest_message": "Bravo, vous êtes à jour.",
|
||||||
"current_version": "Version actuelle",
|
"current_version": "Version actuelle",
|
||||||
"download_zip_file": "Télécharger le fichier ZIP",
|
"download_zip_file": "Télécharger le fichier ZIP",
|
||||||
"unzipping_package": "Dézip du paquet en cours",
|
"unzipping_package": "Dézipper le package",
|
||||||
"copying_files": "Copie des fichiers en cours",
|
"copying_files": "Copie de fichiers en cours",
|
||||||
"deleting_files": "Suppression des fichiers inutilisés",
|
"deleting_files": "Supprimer les fichiers inutilisés",
|
||||||
"running_migrations": "Migrations en cours",
|
"running_migrations": "Migrations en cours",
|
||||||
"finishing_update": "Finalisation de la mise à jour",
|
"finishing_update": "Finalisation de la mise à jour",
|
||||||
"update_failed": "Échec de la mise à jour",
|
"update_failed": "Échec de la mise à jour",
|
||||||
|
|||||||
@ -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": "राज्य",
|
||||||
@ -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,47 +447,47 @@
|
|||||||
"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": "सभी",
|
||||||
"paid": "Paid",
|
"paid": "भुगतान किया गया",
|
||||||
"unpaid": "Unpaid",
|
"unpaid": "अवैतनिक",
|
||||||
"viewed": "Viewed",
|
"viewed": "देखा गया",
|
||||||
"overdue": "Overdue",
|
"overdue": "अतिदेय",
|
||||||
"active": "Active",
|
"active": "सक्रिय",
|
||||||
"completed": "Completed",
|
"completed": "पूर्ण",
|
||||||
"customer": "CUSTOMER",
|
"customer": "ग्राहक",
|
||||||
"paid_status": "PAID STATUS",
|
"paid_status": "भुगतान की स्थिति",
|
||||||
"ref_no": "REF NO.",
|
"ref_no": "प्रसंग संख्या",
|
||||||
"number": "NUMBER",
|
"number": "संख्या",
|
||||||
"amount_due": "AMOUNT DUE",
|
"amount_due": "देय राशि",
|
||||||
"partially_paid": "Partially Paid",
|
"partially_paid": "आंशिक रूप से भुगतान किया",
|
||||||
"total": "Total",
|
"total": "संपूर्ण",
|
||||||
"discount": "Discount",
|
"discount": "छूट",
|
||||||
"sub_total": "Sub Total",
|
"sub_total": "उप-योग",
|
||||||
"invoice": "Recurring Invoice | Recurring Invoices",
|
"invoice": "आवर्ती बिल",
|
||||||
"invoice_number": "Recurring Invoice Number",
|
"invoice_number": "आवर्ती बिल संख्या",
|
||||||
"next_invoice_date": "Next Invoice Date",
|
"next_invoice_date": "अगली बिल तिथि",
|
||||||
"ref_number": "Ref Number",
|
"ref_number": "संदर्भ संख्या",
|
||||||
"contact": "Contact",
|
"contact": "संपर्क",
|
||||||
"add_item": "Add an Item",
|
"add_item": "आइटम जोड़ें",
|
||||||
"date": "Date",
|
"date": "दिनांक",
|
||||||
"limit_by": "Limit by",
|
"limit_by": "द्वारा सीमित करें",
|
||||||
"limit_date": "Limit Date",
|
"limit_date": "सीमा तिथि",
|
||||||
"limit_count": "Limit Count",
|
"limit_count": "सीमा गिनती",
|
||||||
"count": "Count",
|
"count": "गिनती",
|
||||||
"status": "Status",
|
"status": "स्थिति",
|
||||||
"select_a_status": "Select a status",
|
"select_a_status": "स्टेटस चुनें",
|
||||||
"working": "Working",
|
"working": "काम कर रहा है",
|
||||||
"on_hold": "On Hold",
|
"on_hold": "रुका हुआ है",
|
||||||
"complete": "Completed",
|
"complete": "पूर्ण",
|
||||||
"add_tax": "Add Tax",
|
"add_tax": "कर जोड़ें",
|
||||||
"amount": "मात्रा",
|
"amount": "मात्रा",
|
||||||
"action": "कार्य",
|
"action": "कार्य",
|
||||||
"notes": "नोट्स",
|
"notes": "नोट्स",
|
||||||
@ -505,8 +505,8 @@
|
|||||||
"confirm_send": "यह आवर्ती चालान ग्राहक को ईमेल के माध्यम से भेजा जाएगा",
|
"confirm_send": "यह आवर्ती चालान ग्राहक को ईमेल के माध्यम से भेजा जाएगा",
|
||||||
"starts_at": "आरंभ करने की तिथि",
|
"starts_at": "आरंभ करने की तिथि",
|
||||||
"due_date": "बिल की देय तिथि",
|
"due_date": "बिल की देय तिथि",
|
||||||
"record_payment": "Record Payment",
|
"record_payment": "भुगतान रिकॉर्ड करें",
|
||||||
"add_new_invoice": "Add New Recurring Invoice",
|
"add_new_invoice": "आवर्ती बिल फिर से भेजें",
|
||||||
"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",
|
||||||
@ -659,46 +659,46 @@
|
|||||||
},
|
},
|
||||||
"modules": {
|
"modules": {
|
||||||
"buy_now": "Buy Now",
|
"buy_now": "Buy Now",
|
||||||
"install": "Install",
|
"install": "इंस्टॉल",
|
||||||
"price": "Price",
|
"price": "मूल्य",
|
||||||
"download_zip_file": "Download ZIP file",
|
"download_zip_file": "ज़िप डाउनलोड करे",
|
||||||
"unzipping_package": "Unzipping Package",
|
"unzipping_package": "पैकेज खोल रहा है",
|
||||||
"copying_files": "Copying Files",
|
"copying_files": "फ़ाइलें कॉपी हो रही है",
|
||||||
"deleting_files": "Deleting Unused files",
|
"deleting_files": "अप्रयुक्त फाइलों को हटाना",
|
||||||
"completing_installation": "Completing Installation",
|
"completing_installation": "स्थापना पूर्ण करना",
|
||||||
"update_failed": "Update Failed",
|
"update_failed": "अद्यतनीकरण असफल रहा",
|
||||||
"install_success": "Module has been installed successfully!",
|
"install_success": "मॉड्यूल सफलतापूर्वक स्थापित किया गया है!",
|
||||||
"customer_reviews": "Reviews",
|
"customer_reviews": "समीक्षा",
|
||||||
"license": "License",
|
"license": "लाइसेन्स",
|
||||||
"faq": "FAQ",
|
"faq": "हमेशा पूछे जाने वाले प्रश्न",
|
||||||
"monthly": "Monthly",
|
"monthly": "महीने के",
|
||||||
"yearly": "Yearly",
|
"yearly": "हर वर्ष",
|
||||||
"updated": "Updated",
|
"updated": "अपडेट किया गया",
|
||||||
"version": "Version",
|
"version": "वर्ज़न",
|
||||||
"disable": "Disable",
|
"disable": "अक्षम करें",
|
||||||
"module_disabled": "Module Disabled",
|
"module_disabled": "मॉड्यूल अक्षम",
|
||||||
"enable": "Enable",
|
"enable": "सक्षम",
|
||||||
"module_enabled": "Module Enabled",
|
"module_enabled": "मॉड्यूल सक्षम",
|
||||||
"update_to": "Update To",
|
"update_to": "अपडेट करें",
|
||||||
"module_updated": "Module Updated Successfully!",
|
"module_updated": "मॉड्यूल सफलतापूर्वक अपडेट किया गया!",
|
||||||
"title": "Modules",
|
"title": "मॉड्यूल",
|
||||||
"module": "Module | Modules",
|
"module": "मॉड्यूल | मॉड्यूल",
|
||||||
"api_token": "API token",
|
"api_token": "एपीआई टोकन",
|
||||||
"invalid_api_token": "Invalid API Token.",
|
"invalid_api_token": "अमान्य एपीआई टोकन।",
|
||||||
"other_modules": "Other Modules",
|
"other_modules": "अन्य मॉड्यूल",
|
||||||
"view_all": "View All",
|
"view_all": "सभी को देखें",
|
||||||
"no_reviews_found": "There are no reviews for this module yet!",
|
"no_reviews_found": "इस मॉड्युल के लिए अभी तक वहां कोई समीक्षा नहीं है!",
|
||||||
"module_not_purchased": "Module Not Purchased",
|
"module_not_purchased": "मॉड्यूल खरीदा नहीं गया",
|
||||||
"module_not_found": "Module Not Found",
|
"module_not_found": "मॉड्यूल नहीं मिला",
|
||||||
"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": "अंतिम बार अद्यतन किया गया",
|
||||||
"connect_installation": "Connect your installation",
|
"connect_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": "{url} में लॉग इन करें और API टोकन दर्ज करके इस इंस्टॉलेशन को कनेक्ट करें। कनेक्शन स्थापित होने के बाद आपके खरीदे गए मॉड्यूल यहां दिखाई देंगे।",
|
||||||
"view_module": "View Module",
|
"view_module": "मॉड्यूल देखें",
|
||||||
"update_available": "Update Available",
|
"update_available": "उपलब्ध अद्यतन",
|
||||||
"purchased": "Purchased",
|
"purchased": "खरीदी",
|
||||||
"installed": "Installed",
|
"installed": "इंस्टॉल हुआ।",
|
||||||
"no_modules_installed": "No Modules Installed Yet!",
|
"no_modules_installed": "अभी तक कोई मॉड्यूल स्थापित नहीं है!",
|
||||||
"disable_warning": "All the settings for this particular will be reverted.",
|
"disable_warning": "All the settings for this particular will be reverted.",
|
||||||
"what_you_get": "What you get"
|
"what_you_get": "What you get"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -93,14 +93,14 @@
|
|||||||
"no_note_found": "Tidak ada catatan yang ditemukan",
|
"no_note_found": "Tidak ada catatan yang ditemukan",
|
||||||
"insert_note": "Sisipkan Catatan",
|
"insert_note": "Sisipkan Catatan",
|
||||||
"copied_pdf_url_clipboard": "URL file PDF disalin ke clipboard!",
|
"copied_pdf_url_clipboard": "URL file PDF disalin ke clipboard!",
|
||||||
"copied_url_clipboard": "Copied url to clipboard!",
|
"copied_url_clipboard": "Disalin ke clipboard!",
|
||||||
"docs": "Dokumen",
|
"docs": "Dokumen",
|
||||||
"do_you_wish_to_continue": "Apakah anda ingin melanjutkan?",
|
"do_you_wish_to_continue": "Apakah anda ingin melanjutkan?",
|
||||||
"note": "Catatan",
|
"note": "Catatan",
|
||||||
"pay_invoice": "Pay Invoice",
|
"pay_invoice": "Bayar tagihan",
|
||||||
"login_successfully": "Logged in successfully!",
|
"login_successfully": "Login berhasil!",
|
||||||
"logged_out_successfully": "Logged out successfully",
|
"logged_out_successfully": "Berhasil keluar",
|
||||||
"mark_as_default": "Mark as default"
|
"mark_as_default": "Tandai sebagai default"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"select_year": "Pilih tahun",
|
"select_year": "Pilih tahun",
|
||||||
@ -109,7 +109,7 @@
|
|||||||
"customers": "Pelanggan",
|
"customers": "Pelanggan",
|
||||||
"invoices": "Faktur",
|
"invoices": "Faktur",
|
||||||
"estimates": "Perkiraan",
|
"estimates": "Perkiraan",
|
||||||
"payments": "Payments"
|
"payments": "Pembayaran"
|
||||||
},
|
},
|
||||||
"chart_info": {
|
"chart_info": {
|
||||||
"total_sales": "Penjualan",
|
"total_sales": "Penjualan",
|
||||||
@ -208,10 +208,10 @@
|
|||||||
"new_customer": "Pelanggan Baru",
|
"new_customer": "Pelanggan Baru",
|
||||||
"edit_customer": "Ubah Pelanggan",
|
"edit_customer": "Ubah Pelanggan",
|
||||||
"basic_info": "Info dasar",
|
"basic_info": "Info dasar",
|
||||||
"portal_access": "Portal Access",
|
"portal_access": "Akses Portal",
|
||||||
"portal_access_text": "Would you like to allow this customer to login to the Customer Portal?",
|
"portal_access_text": "Apakah Anda ingin mengizinkan pelanggan ini untuk masuk ke Portal Pelanggan?",
|
||||||
"portal_access_url": "Customer Portal Login URL",
|
"portal_access_url": "URL Masuk Portal Pelanggan",
|
||||||
"portal_access_url_help": "Please copy & forward the above given URL to your customer for providing access.",
|
"portal_access_url_help": "Harap salin & teruskan URL yang diberikan di atas kepada pelanggan Anda untuk memberikan akses.",
|
||||||
"billing_address": "Alamat Tagihan",
|
"billing_address": "Alamat Tagihan",
|
||||||
"shipping_address": "Alamat Pengiriman",
|
"shipping_address": "Alamat Pengiriman",
|
||||||
"copy_billing_address": "Menyalin dari Tagihan",
|
"copy_billing_address": "Menyalin dari Tagihan",
|
||||||
@ -231,7 +231,7 @@
|
|||||||
"confirm_delete": "Anda tidak akan dapat mengembalikan pelanggan dan semua tagihan terkait. | Anda tidak akan dapat mengembalikan pelanggan dan semua Tagihan terkait, Penawaran dan Pembayaran.",
|
"confirm_delete": "Anda tidak akan dapat mengembalikan pelanggan dan semua tagihan terkait. | Anda tidak akan dapat mengembalikan pelanggan dan semua Tagihan terkait, Penawaran dan Pembayaran.",
|
||||||
"created_message": "Pelanggan berhasil dibuat",
|
"created_message": "Pelanggan berhasil dibuat",
|
||||||
"updated_message": "Pelanggan berhasil diperbarui",
|
"updated_message": "Pelanggan berhasil diperbarui",
|
||||||
"address_updated_message": "Address Information Updated succesfully",
|
"address_updated_message": "Informasi Alamat Berhasil Diperbarui",
|
||||||
"deleted_message": "Pelanggan berhasil dihapus",
|
"deleted_message": "Pelanggan berhasil dihapus",
|
||||||
"edit_currency_not_allowed": "Ketika transaksi telah dibuat, mata uang tidak dapat dirubah."
|
"edit_currency_not_allowed": "Ketika transaksi telah dibuat, mata uang tidak dapat dirubah."
|
||||||
},
|
},
|
||||||
@ -265,8 +265,8 @@
|
|||||||
},
|
},
|
||||||
"estimates": {
|
"estimates": {
|
||||||
"title": "Perkiraan",
|
"title": "Perkiraan",
|
||||||
"accept_estimate": "Accept Estimate",
|
"accept_estimate": "Perkiraan",
|
||||||
"reject_estimate": "Reject Estimate",
|
"reject_estimate": "Tolak Perkiraan",
|
||||||
"estimate": "Estimasi",
|
"estimate": "Estimasi",
|
||||||
"estimates_list": "Daftar Penawaran",
|
"estimates_list": "Daftar Penawaran",
|
||||||
"days": "{days} Hari",
|
"days": "{days} Hari",
|
||||||
@ -276,7 +276,7 @@
|
|||||||
"paid": "Lunas",
|
"paid": "Lunas",
|
||||||
"unpaid": "Belum lunas",
|
"unpaid": "Belum lunas",
|
||||||
"customer": "PELANGGAN",
|
"customer": "PELANGGAN",
|
||||||
"ref_no": "REF NO.",
|
"ref_no": "NO. REF.",
|
||||||
"number": "NOMOR",
|
"number": "NOMOR",
|
||||||
"amount_due": "Jumlah yang harus dibayar",
|
"amount_due": "Jumlah yang harus dibayar",
|
||||||
"partially_paid": "Pembayaran Sebagian",
|
"partially_paid": "Pembayaran Sebagian",
|
||||||
@ -318,10 +318,10 @@
|
|||||||
},
|
},
|
||||||
"accepted": "Diterima",
|
"accepted": "Diterima",
|
||||||
"rejected": "Ditolak",
|
"rejected": "Ditolak",
|
||||||
"expired": "Expired",
|
"expired": "Kadaluarsa",
|
||||||
"sent": "Terkirim",
|
"sent": "Terkirim",
|
||||||
"draft": "Draf",
|
"draft": "Draf",
|
||||||
"viewed": "Viewed",
|
"viewed": "Dilihat",
|
||||||
"declined": "Ditolak",
|
"declined": "Ditolak",
|
||||||
"new_estimate": "Penawaran Baru",
|
"new_estimate": "Penawaran Baru",
|
||||||
"add_new_estimate": "Tambah Penawaran Baru",
|
"add_new_estimate": "Tambah Penawaran Baru",
|
||||||
@ -355,14 +355,14 @@
|
|||||||
"select_an_item": "Ketik atau klik untuk memilih",
|
"select_an_item": "Ketik atau klik untuk memilih",
|
||||||
"type_item_description": "Ketik Deskripsi Item (opsional)"
|
"type_item_description": "Ketik Deskripsi Item (opsional)"
|
||||||
},
|
},
|
||||||
"mark_as_default_estimate_template_description": "If enabled, the selected template will be automatically selected for new estimates."
|
"mark_as_default_estimate_template_description": "Jika diaktifkan, template terpilih akan secara otomatis digunakan saat pembuatan estimate baru."
|
||||||
},
|
},
|
||||||
"invoices": {
|
"invoices": {
|
||||||
"title": "Faktur",
|
"title": "Faktur",
|
||||||
"download": "Download",
|
"download": "Unduh",
|
||||||
"pay_invoice": "Pay Invoice",
|
"pay_invoice": "Bayar tagihan",
|
||||||
"invoices_list": "Daftar Faktur",
|
"invoices_list": "Daftar Faktur",
|
||||||
"invoice_information": "Invoice Information",
|
"invoice_information": "Informasi tagihan",
|
||||||
"days": "{days} Hari",
|
"days": "{days} Hari",
|
||||||
"months": "{months} Bulan",
|
"months": "{months} Bulan",
|
||||||
"years": "{years} Tahun",
|
"years": "{years} Tahun",
|
||||||
@ -374,7 +374,7 @@
|
|||||||
"completed": "Selesai",
|
"completed": "Selesai",
|
||||||
"customer": "PELANGGAN",
|
"customer": "PELANGGAN",
|
||||||
"paid_status": "STATUS PEMBAYARAN",
|
"paid_status": "STATUS PEMBAYARAN",
|
||||||
"ref_no": "REF NO.",
|
"ref_no": "NO. REF.",
|
||||||
"number": "NOMOR",
|
"number": "NOMOR",
|
||||||
"amount_due": "Jumlah yang harus dibayar",
|
"amount_due": "Jumlah yang harus dibayar",
|
||||||
"partially_paid": "Pembayaran Sebagian",
|
"partially_paid": "Pembayaran Sebagian",
|
||||||
@ -439,19 +439,19 @@
|
|||||||
"select_an_item": "Ketik atau klik untuk memilih",
|
"select_an_item": "Ketik atau klik untuk memilih",
|
||||||
"type_item_description": "Ketik Deskripsi Item (opsional)"
|
"type_item_description": "Ketik Deskripsi Item (opsional)"
|
||||||
},
|
},
|
||||||
"payment_attached_message": "One of the selected invoices already have a payment attached to it. Make sure to delete the attached payments first in order to go ahead with the removal",
|
"payment_attached_message": "Salah satu faktur yang dipilih sudah memiliki pembayaran yang menyertainya. Pastikan untuk menghapus pembayaran terlampir terlebih dahulu untuk melanjutkan penghapusan",
|
||||||
"confirm_delete": "You will not be able to recover this Invoice | You will not be able to recover these Invoices",
|
"confirm_delete": "Anda tidak akan dapat memulihkan Faktur ini | Anda tidak akan dapat memulihkan Faktur ini",
|
||||||
"created_message": "Invoice created successfully",
|
"created_message": "Faktur berhasil dibuat",
|
||||||
"updated_message": "Invoice updated successfully",
|
"updated_message": "Faktur berhasil diperbarui",
|
||||||
"deleted_message": "Invoice deleted successfully | Invoices deleted successfully",
|
"deleted_message": "Faktur berhasil dihapus | Faktur berhasil dihapus",
|
||||||
"marked_as_sent_message": "Invoice marked as sent successfully",
|
"marked_as_sent_message": "Tandai Faktur sudah dikirim",
|
||||||
"something_went_wrong": "something went wrong",
|
"something_went_wrong": "terjadi kesalahan",
|
||||||
"invalid_due_amount_message": "Total Invoice amount cannot be less than total paid amount for this Invoice. Please update the invoice or delete the associated payments to continue.",
|
"invalid_due_amount_message": "Jumlah Total Faktur tidak boleh kurang dari jumlah total yang dibayarkan untuk Faktur ini. Harap perbarui faktur atau hapus pembayaran terkait untuk melanjutkan.",
|
||||||
"mark_as_default_invoice_template_description": "If enabled, the selected template will be automatically selected for new invoices."
|
"mark_as_default_invoice_template_description": "Jika diaktifkan, template terpilih akan secara otomatis digunakan saat pembuatan estimate baru."
|
||||||
},
|
},
|
||||||
"recurring_invoices": {
|
"recurring_invoices": {
|
||||||
"title": "Recurring Invoices",
|
"title": "Tagihan-Tagihan Berulang",
|
||||||
"invoices_list": "Recurring Invoices List",
|
"invoices_list": "Daftar Faktur Berulang",
|
||||||
"days": "{days} Hari",
|
"days": "{days} Hari",
|
||||||
"months": "{months} Bulan",
|
"months": "{months} Bulan",
|
||||||
"years": "{years} Tahun",
|
"years": "{years} Tahun",
|
||||||
@ -459,61 +459,61 @@
|
|||||||
"paid": "Lunas",
|
"paid": "Lunas",
|
||||||
"unpaid": "Belum lunas",
|
"unpaid": "Belum lunas",
|
||||||
"viewed": "Dilihat",
|
"viewed": "Dilihat",
|
||||||
"overdue": "Overdue",
|
"overdue": "Lewat jatuh tempo",
|
||||||
"active": "Aktif",
|
"active": "Aktif",
|
||||||
"completed": "Selesai",
|
"completed": "Selesai",
|
||||||
"customer": "PELANGGAN",
|
"customer": "PELANGGAN",
|
||||||
"paid_status": "PAID STATUS",
|
"paid_status": "STATUS PEMBAYARAN",
|
||||||
"ref_no": "REF NO.",
|
"ref_no": "NO. REF.",
|
||||||
"number": "NOMOR",
|
"number": "NOMOR",
|
||||||
"amount_due": "AMOUNT DUE",
|
"amount_due": "Jumlah yang harus dibayar",
|
||||||
"partially_paid": "Partially Paid",
|
"partially_paid": "Angsuran",
|
||||||
"total": "Total",
|
"total": "Total",
|
||||||
"discount": "Diskon",
|
"discount": "Diskon",
|
||||||
"sub_total": "Sub Total",
|
"sub_total": "Sub Total",
|
||||||
"invoice": "Recurring Invoice | Recurring Invoices",
|
"invoice": "Faktur Berulang | Faktur Berulang",
|
||||||
"invoice_number": "Recurring Invoice Number",
|
"invoice_number": "Nomor Faktur Berulang",
|
||||||
"next_invoice_date": "Next Invoice Date",
|
"next_invoice_date": "Tanggal Faktur Berikutnya",
|
||||||
"ref_number": "Ref Number",
|
"ref_number": "Nomor Referensi",
|
||||||
"contact": "Kontak",
|
"contact": "Kontak",
|
||||||
"add_item": "Tambah Barang",
|
"add_item": "Tambah Barang",
|
||||||
"date": "Tanggal",
|
"date": "Tanggal",
|
||||||
"limit_by": "Limit by",
|
"limit_by": "Batasi oleh",
|
||||||
"limit_date": "Limit Date",
|
"limit_date": "Batas Tanggal",
|
||||||
"limit_count": "Limit Count",
|
"limit_count": "Batas Jumlah",
|
||||||
"count": "Count",
|
"count": "Hitung",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"select_a_status": "Pilih status",
|
"select_a_status": "Pilih status",
|
||||||
"working": "Working",
|
"working": "Sedang mengerjakan",
|
||||||
"on_hold": "On Hold",
|
"on_hold": "Ditangguhkan",
|
||||||
"complete": "Selesai",
|
"complete": "Selesai",
|
||||||
"add_tax": "Tambah Pajak",
|
"add_tax": "Tambah Pajak",
|
||||||
"amount": "Jumlah",
|
"amount": "Jumlah",
|
||||||
"action": "Aksi",
|
"action": "Aksi",
|
||||||
"notes": "Catatan",
|
"notes": "Catatan",
|
||||||
"view": "View",
|
"view": "Tampilan",
|
||||||
"basic_info": "Basic Info",
|
"basic_info": "Informasi dasar",
|
||||||
"send_invoice": "Send Recurring Invoice",
|
"send_invoice": "Kirim Ulang Faktur Berulang",
|
||||||
"auto_send": "Auto Send",
|
"auto_send": "Kirim Otomatis",
|
||||||
"resend_invoice": "Resend Recurring Invoice",
|
"resend_invoice": "Kirim Ulang Faktur Berulang",
|
||||||
"invoice_template": "Recurring Invoice Template",
|
"invoice_template": "Nomor Faktur Berulang",
|
||||||
"conversion_message": "Recurring Invoice cloned successful",
|
"conversion_message": "Faktur Berulang berhasil dikloning",
|
||||||
"template": "Template",
|
"template": "Template",
|
||||||
"mark_as_sent": "Mark as sent",
|
"mark_as_sent": "Tandai sebagai terkirim",
|
||||||
"confirm_send_invoice": "This recurring invoice will be sent via email to the customer",
|
"confirm_send_invoice": "Faktur berulang ini akan dikirim melalui email ke pelanggan",
|
||||||
"invoice_mark_as_sent": "This recurring invoice will be marked as sent",
|
"invoice_mark_as_sent": "Faktur berulang ini akan ditandai sebagai terkirim",
|
||||||
"confirm_send": "This recurring invoice will be sent via email to the customer",
|
"confirm_send": "Faktur berulang ini akan dikirim melalui email ke pelanggan",
|
||||||
"starts_at": "Tanggal Mulai",
|
"starts_at": "Tanggal Mulai",
|
||||||
"due_date": "Invoice Due Date",
|
"due_date": "Tanggal Jatuh Tempo Faktur",
|
||||||
"record_payment": "Record Payment",
|
"record_payment": "Rekam Pembayaran",
|
||||||
"add_new_invoice": "Add New Recurring Invoice",
|
"add_new_invoice": "Tambahkan Faktur Berulang Baru",
|
||||||
"update_expense": "Update Expense",
|
"update_expense": "Perbarui Biaya",
|
||||||
"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",
|
||||||
"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": "Perbarui Faktur Berulang",
|
||||||
"add_new_tax": "Tambah Pajak Baru",
|
"add_new_tax": "Tambah Pajak Baru",
|
||||||
"no_invoices": "Belum ada Faktur Berulang!",
|
"no_invoices": "Belum ada Faktur Berulang!",
|
||||||
"mark_as_rejected": "Ditandai telah ditolak",
|
"mark_as_rejected": "Ditandai telah ditolak",
|
||||||
@ -526,7 +526,7 @@
|
|||||||
"cloned_successfully": "Faktur Berulang berhasil digandakan",
|
"cloned_successfully": "Faktur Berulang berhasil digandakan",
|
||||||
"clone_invoice": "Gandakan Faktur Berulang",
|
"clone_invoice": "Gandakan Faktur Berulang",
|
||||||
"confirm_clone": "Faktur Berulang ini akan digandakan menjadi Faktur Berulang yang baru",
|
"confirm_clone": "Faktur Berulang ini akan digandakan menjadi Faktur Berulang yang baru",
|
||||||
"add_customer_email": "Please add an email address for this customer to send invoices automatically.",
|
"add_customer_email": "Tambahkan alamat email pelanggan untuk mengirimkan tagihan secara otomatis.",
|
||||||
"item": {
|
"item": {
|
||||||
"title": "Judul item",
|
"title": "Judul item",
|
||||||
"description": "Deskripsi",
|
"description": "Deskripsi",
|
||||||
@ -550,19 +550,19 @@
|
|||||||
"month": "Bulan",
|
"month": "Bulan",
|
||||||
"day_week": "Hari dalam minggu"
|
"day_week": "Hari dalam minggu"
|
||||||
},
|
},
|
||||||
"confirm_delete": "You will not be able to recover this Invoice | You will not be able to recover these Invoices",
|
"confirm_delete": "Anda tidak akan dapat memulihkan Faktur ini | Anda tidak akan dapat memulihkan Faktur ini",
|
||||||
"created_message": "Recurring Invoice created successfully",
|
"created_message": "Faktur Berulang berhasil dibuat",
|
||||||
"updated_message": "Recurring Invoice updated successfully",
|
"updated_message": "Faktur Berulang berhasil diperbaharui",
|
||||||
"deleted_message": "Recurring Invoice deleted successfully | Recurring Invoices deleted successfully",
|
"deleted_message": "Faktur Berulang berhasil dihapus | Faktur Berulang berhasil dihapus",
|
||||||
"marked_as_sent_message": "Recurring Invoice marked as sent successfully",
|
"marked_as_sent_message": "Tandai Faktur Berulang sudah dikirim",
|
||||||
"user_email_does_not_exist": "User email does not exist",
|
"user_email_does_not_exist": "Email pengguna tidak ada",
|
||||||
"something_went_wrong": "something went wrong",
|
"something_went_wrong": "terjadi kesalahan",
|
||||||
"invalid_due_amount_message": "Total Recurring Invoice amount cannot be less than total paid amount for this Recurring Invoice. Please update the invoice or delete the associated payments to continue."
|
"invalid_due_amount_message": "Jumlah Total Faktur Berulang tidak boleh kurang dari jumlah total yang dibayarkan untuk Faktur Berulang ini. Harap perbarui faktur atau hapus pembayaran terkait untuk melanjutkan."
|
||||||
},
|
},
|
||||||
"payments": {
|
"payments": {
|
||||||
"title": "Pembayaran",
|
"title": "Pembayaran",
|
||||||
"payments_list": "Daftar Pembayaran",
|
"payments_list": "Daftar Pembayaran",
|
||||||
"record_payment": "Record Payment",
|
"record_payment": "Rekam Pembayaran",
|
||||||
"customer": "Pelanggan",
|
"customer": "Pelanggan",
|
||||||
"date": "Tanggal",
|
"date": "Tanggal",
|
||||||
"amount": "Jumlah",
|
"amount": "Jumlah",
|
||||||
@ -573,29 +573,29 @@
|
|||||||
"note": "Catatan",
|
"note": "Catatan",
|
||||||
"add_payment": "Tambah Pembayaran",
|
"add_payment": "Tambah Pembayaran",
|
||||||
"new_payment": "Pembayaran Baru",
|
"new_payment": "Pembayaran Baru",
|
||||||
"edit_payment": "Edit Payment",
|
"edit_payment": "Edit Pembayaran",
|
||||||
"view_payment": "View Payment",
|
"view_payment": "Lihat Pembayaran",
|
||||||
"add_new_payment": "Add New Payment",
|
"add_new_payment": "Tambahkan Pembayaran Baru",
|
||||||
"send_payment_receipt": "Send Payment Receipt",
|
"send_payment_receipt": "Kirim Tanda Terima Pembayaran",
|
||||||
"send_payment": "Send Payment",
|
"send_payment": "Kirim Pembayaran",
|
||||||
"save_payment": "Save Payment",
|
"save_payment": "Simpan Pembayaran",
|
||||||
"update_payment": "Update Payment",
|
"update_payment": "Perbaharui Pembayaran",
|
||||||
"payment": "Pembayaran",
|
"payment": "Pembayaran",
|
||||||
"no_payments": "No payments yet!",
|
"no_payments": "Belum ada pembayaran!",
|
||||||
"not_selected": "Tidak dipilih",
|
"not_selected": "Tidak dipilih",
|
||||||
"no_invoice": "Tidak ada faktur",
|
"no_invoice": "Tidak ada faktur",
|
||||||
"no_matching_payments": "There are no matching payments!",
|
"no_matching_payments": "Tidak ada pembayaran yang cocok!",
|
||||||
"list_of_payments": "This section will contain the list of payments.",
|
"list_of_payments": "Bagian ini akan berisi daftar pembayaran.",
|
||||||
"select_payment_mode": "Select payment mode",
|
"select_payment_mode": "Pilih mode pembayaran",
|
||||||
"confirm_mark_as_sent": "This estimate will be marked as sent",
|
"confirm_mark_as_sent": "Penawaran ini akan ditandai telah dikirim",
|
||||||
"confirm_send_payment": "This payment will be sent via email to the customer",
|
"confirm_send_payment": "Pembayaran ini akan dikirim melalui email ke pelanggan",
|
||||||
"send_payment_successfully": "Payment sent successfully",
|
"send_payment_successfully": "Pembayaran berhasil dikirim",
|
||||||
"something_went_wrong": "something went wrong",
|
"something_went_wrong": "terjadi kesalahan",
|
||||||
"confirm_delete": "You will not be able to recover this Payment | You will not be able to recover these Payments",
|
"confirm_delete": "Anda tidak akan dapat memulihkan Pembayaran ini | Anda tidak akan dapat memulihkan Pembayaran ini",
|
||||||
"created_message": "Payment created successfully",
|
"created_message": "Pembayaran berhasil dibuat",
|
||||||
"updated_message": "Payment updated successfully",
|
"updated_message": "Pembayaran berhasil diperbaharui",
|
||||||
"deleted_message": "Payment deleted successfully | Payments deleted successfully",
|
"deleted_message": "Pembayaran berhasil dihapus | Pembayaran berhasil dihapus",
|
||||||
"invalid_amount_message": "Payment amount is invalid"
|
"invalid_amount_message": "Jumlah pembayaran tidak valid"
|
||||||
},
|
},
|
||||||
"expenses": {
|
"expenses": {
|
||||||
"title": "Pengeluaran",
|
"title": "Pengeluaran",
|
||||||
@ -610,29 +610,29 @@
|
|||||||
"to_date": "Sampai Tanggal",
|
"to_date": "Sampai Tanggal",
|
||||||
"expense_date": "Tanggal",
|
"expense_date": "Tanggal",
|
||||||
"description": "Deskripsi",
|
"description": "Deskripsi",
|
||||||
"receipt": "Receipt",
|
"receipt": "Tanda Terima",
|
||||||
"amount": "Jumlah",
|
"amount": "Jumlah",
|
||||||
"action": "Aksi",
|
"action": "Aksi",
|
||||||
"not_selected": "Tidak dipilih",
|
"not_selected": "Tidak dipilih",
|
||||||
"note": "Catatan",
|
"note": "Catatan",
|
||||||
"category_id": "Category Id",
|
"category_id": "Id kategori",
|
||||||
"date": "Tanggal",
|
"date": "Tanggal",
|
||||||
"add_expense": "Add Expense",
|
"add_expense": "Tambahkan pengeluaran",
|
||||||
"add_new_expense": "Add New Expense",
|
"add_new_expense": "Tambah Pengeluaran Baru",
|
||||||
"save_expense": "Save Expense",
|
"save_expense": "Simpan Pengeluaran",
|
||||||
"update_expense": "Update Expense",
|
"update_expense": "Edit Pengeluaran",
|
||||||
"download_receipt": "Download Receipt",
|
"download_receipt": "Unduh Tanda Terima",
|
||||||
"edit_expense": "Edit Expense",
|
"edit_expense": "Edit Pengeluaran",
|
||||||
"new_expense": "New Expense",
|
"new_expense": "Pengeluaran Baru",
|
||||||
"expense": "Expense | Expenses",
|
"expense": "Biaya | Pengeluaran",
|
||||||
"no_expenses": "No expenses yet!",
|
"no_expenses": "Belum ada pengeluaran!",
|
||||||
"list_of_expenses": "This section will contain the list of expenses.",
|
"list_of_expenses": "Bagian ini akan berisi daftar pengeluaran.",
|
||||||
"confirm_delete": "You will not be able to recover this Expense | You will not be able to recover these Expenses",
|
"confirm_delete": "Anda tidak akan dapat memulihkan Pengeluaran ini | Anda tidak akan dapat memulihkan Pengeluaran ini",
|
||||||
"created_message": "Expense created successfully",
|
"created_message": "Pengeluaran berhasil dibuat",
|
||||||
"updated_message": "Expense updated successfully",
|
"updated_message": "Pengeluaran berhasil diperbaharui",
|
||||||
"deleted_message": "Expense deleted successfully | Expenses deleted successfully",
|
"deleted_message": "Pengeluaran berhasil dihapus | Pengeluaran berhasil dihapus",
|
||||||
"categories": {
|
"categories": {
|
||||||
"categories_list": "Categories List",
|
"categories_list": "Daftar Kategori",
|
||||||
"title": "Title",
|
"title": "Title",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"description": "Description",
|
"description": "Description",
|
||||||
@ -694,65 +694,65 @@
|
|||||||
"last_updated": "Last Updated On",
|
"last_updated": "Last Updated On",
|
||||||
"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": "Lihat Module",
|
||||||
"update_available": "Update Available",
|
"update_available": "Pembaruan Tersedia",
|
||||||
"purchased": "Purchased",
|
"purchased": "Pembelian",
|
||||||
"installed": "Installed",
|
"installed": "Terinstal",
|
||||||
"no_modules_installed": "No Modules Installed Yet!",
|
"no_modules_installed": "Belum Ada Modul yang Terpasang!",
|
||||||
"disable_warning": "All the settings for this particular will be reverted.",
|
"disable_warning": "Semua pengaturan untuk saat ini akan dikembalikan.",
|
||||||
"what_you_get": "What you get"
|
"what_you_get": "Apa yang bisa Anda dapatkan"
|
||||||
},
|
},
|
||||||
"users": {
|
"users": {
|
||||||
"title": "Users",
|
"title": "Pengguna",
|
||||||
"users_list": "Users List",
|
"users_list": "Daftar Pengguna",
|
||||||
"name": "Name",
|
"name": "Nama",
|
||||||
"description": "Description",
|
"description": "Deskripsi",
|
||||||
"added_on": "Added On",
|
"added_on": "Ditambahkan Pada",
|
||||||
"date_of_creation": "Date Of Creation",
|
"date_of_creation": "Tanggal pembuatan",
|
||||||
"action": "Action",
|
"action": "Aksi",
|
||||||
"add_user": "Add User",
|
"add_user": "Tambah Pengguna",
|
||||||
"save_user": "Save User",
|
"save_user": "Simpan Pengguna",
|
||||||
"update_user": "Update User",
|
"update_user": "Edit Pengguna",
|
||||||
"user": "User | Users",
|
"user": "Pengguna | Pengguna",
|
||||||
"add_new_user": "Add New User",
|
"add_new_user": "Tambahkan pengguna baru",
|
||||||
"new_user": "New User",
|
"new_user": "Pengguna baru",
|
||||||
"edit_user": "Edit User",
|
"edit_user": "Edit Pengguna",
|
||||||
"no_users": "No users yet!",
|
"no_users": "Belum ada pengguna!",
|
||||||
"list_of_users": "This section will contain the list of users.",
|
"list_of_users": "Bagian ini akan berisi daftar pengguna.",
|
||||||
"email": "Email",
|
"email": "Email",
|
||||||
"phone": "Telepon",
|
"phone": "Telepon",
|
||||||
"password": "Kata Sandi",
|
"password": "Kata Sandi",
|
||||||
"user_attached_message": "Cannot delete an item which is already in use",
|
"user_attached_message": "Tidak dapat menghapus item yang sudah digunakan",
|
||||||
"confirm_delete": "You will not be able to recover this User | You will not be able to recover these Users",
|
"confirm_delete": "Anda tidak akan dapat memulihkan Pengguna ini | Anda tidak akan dapat memulihkan Pengguna ini",
|
||||||
"created_message": "User created successfully",
|
"created_message": "Pengguna berhasil dibuat",
|
||||||
"updated_message": "User updated successfully",
|
"updated_message": "Pengguna berhasil diedit",
|
||||||
"deleted_message": "User deleted successfully | Users deleted successfully",
|
"deleted_message": "Pengguna berhasil dihapus | Pengguna berhasil dihapus",
|
||||||
"select_company_role": "Select Role for {company}",
|
"select_company_role": "Pilih Peran untuk {company}",
|
||||||
"companies": "Companies"
|
"companies": "Perusahaan"
|
||||||
},
|
},
|
||||||
"reports": {
|
"reports": {
|
||||||
"title": "Report",
|
"title": "Laporan",
|
||||||
"from_date": "From Date",
|
"from_date": "Dari tanggal",
|
||||||
"to_date": "To Date",
|
"to_date": "Sampai tanggal",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"paid": "Paid",
|
"paid": "Lunas",
|
||||||
"unpaid": "Unpaid",
|
"unpaid": "Belum dibayar",
|
||||||
"download_pdf": "Download PDF",
|
"download_pdf": "Download PDF",
|
||||||
"view_pdf": "View PDF",
|
"view_pdf": "Lihat PDF",
|
||||||
"update_report": "Update Report",
|
"update_report": "Update Laporan",
|
||||||
"report": "Report | Reports",
|
"report": "Laporan | Laporan",
|
||||||
"profit_loss": {
|
"profit_loss": {
|
||||||
"profit_loss": "Profit & Loss",
|
"profit_loss": "Laba rugi",
|
||||||
"to_date": "To Date",
|
"to_date": "Sampai tanggal",
|
||||||
"from_date": "From Date",
|
"from_date": "Dari tanggal",
|
||||||
"date_range": "Select Date Range"
|
"date_range": "Pilih Rentang Tanggal"
|
||||||
},
|
},
|
||||||
"sales": {
|
"sales": {
|
||||||
"sales": "Sales",
|
"sales": "Penjualan",
|
||||||
"date_range": "Select Date Range",
|
"date_range": "Pilih Rentang Tanggal",
|
||||||
"to_date": "To Date",
|
"to_date": "Sampai tanggal",
|
||||||
"from_date": "From Date",
|
"from_date": "Dari tanggal",
|
||||||
"report_type": "Report Type"
|
"report_type": "Jenis laporan"
|
||||||
},
|
},
|
||||||
"taxes": {
|
"taxes": {
|
||||||
"taxes": "Taxes",
|
"taxes": "Taxes",
|
||||||
|
|||||||
@ -17,6 +17,7 @@ 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,
|
||||||
@ -37,5 +38,6 @@ export default {
|
|||||||
vi,
|
vi,
|
||||||
pl,
|
pl,
|
||||||
el,
|
el,
|
||||||
hr
|
hr,
|
||||||
|
th
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
"settings": "Nustatymai",
|
"settings": "Nustatymai",
|
||||||
"logout": "Atsijungti",
|
"logout": "Atsijungti",
|
||||||
"users": "Vartotojai",
|
"users": "Vartotojai",
|
||||||
"modules": "Modules"
|
"modules": "Moduliai"
|
||||||
},
|
},
|
||||||
"general": {
|
"general": {
|
||||||
"add_company": "Pridėti įmonę",
|
"add_company": "Pridėti įmonę",
|
||||||
@ -93,14 +93,14 @@
|
|||||||
"no_note_found": "Jokių žinučių nerasta",
|
"no_note_found": "Jokių žinučių nerasta",
|
||||||
"insert_note": "Terpti prierašą",
|
"insert_note": "Terpti prierašą",
|
||||||
"copied_pdf_url_clipboard": "Nukopijuotas PDF url į iškarpinę!",
|
"copied_pdf_url_clipboard": "Nukopijuotas PDF url į iškarpinę!",
|
||||||
"copied_url_clipboard": "Copied url to clipboard!",
|
"copied_url_clipboard": "Nuoroda nukopijuota!",
|
||||||
"docs": "Dokumentacija",
|
"docs": "Dokumentacija",
|
||||||
"do_you_wish_to_continue": "Ar norite tęsti?",
|
"do_you_wish_to_continue": "Ar norite tęsti?",
|
||||||
"note": "Užrašas",
|
"note": "Užrašas",
|
||||||
"pay_invoice": "Pay Invoice",
|
"pay_invoice": "Apmokėti",
|
||||||
"login_successfully": "Logged in successfully!",
|
"login_successfully": "Prisijungta sėkmingai!",
|
||||||
"logged_out_successfully": "Logged out successfully",
|
"logged_out_successfully": "Atsijungta sėkmingai",
|
||||||
"mark_as_default": "Mark as default"
|
"mark_as_default": "Pažymėti kaip numatytąjį"
|
||||||
},
|
},
|
||||||
"dashboard": {
|
"dashboard": {
|
||||||
"select_year": "Pasirinkite metus",
|
"select_year": "Pasirinkite metus",
|
||||||
@ -109,7 +109,7 @@
|
|||||||
"customers": "Klientai",
|
"customers": "Klientai",
|
||||||
"invoices": "Sąskaitos",
|
"invoices": "Sąskaitos",
|
||||||
"estimates": "Įverčiai",
|
"estimates": "Įverčiai",
|
||||||
"payments": "Payments"
|
"payments": "Mokėjimai"
|
||||||
},
|
},
|
||||||
"chart_info": {
|
"chart_info": {
|
||||||
"total_sales": "Pardavimai",
|
"total_sales": "Pardavimai",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -29,7 +29,7 @@
|
|||||||
"to_date": "До даты",
|
"to_date": "До даты",
|
||||||
"from": "Отправитель",
|
"from": "Отправитель",
|
||||||
"to": "Получатель",
|
"to": "Получатель",
|
||||||
"ok": "Ok",
|
"ok": "Ок",
|
||||||
"yes": "Да",
|
"yes": "Да",
|
||||||
"no": "Нет",
|
"no": "Нет",
|
||||||
"sort_by": "Сортировать",
|
"sort_by": "Сортировать",
|
||||||
@ -97,10 +97,10 @@
|
|||||||
"docs": "Docs",
|
"docs": "Docs",
|
||||||
"do_you_wish_to_continue": "Хотите продолжить?",
|
"do_you_wish_to_continue": "Хотите продолжить?",
|
||||||
"note": "Note",
|
"note": "Note",
|
||||||
"pay_invoice": "Pay Invoice",
|
"pay_invoice": "Оплатить счет",
|
||||||
"login_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,17 +151,17 @@
|
|||||||
"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": "Сегодня",
|
||||||
"this_week": "На этой неделе",
|
"this_week": "На этой неделе",
|
||||||
"this_month": "В этом месяце",
|
"this_month": "В этом месяце",
|
||||||
"this_quarter": "This Quarter",
|
"this_quarter": "Текущий квартал",
|
||||||
"this_year": "В этом году",
|
"this_year": "В этом году",
|
||||||
"previous_week": "Предыдущая неделя",
|
"previous_week": "Предыдущая неделя",
|
||||||
"previous_month": "Предыдущий месяц",
|
"previous_month": "Предыдущий месяц",
|
||||||
@ -171,7 +171,7 @@
|
|||||||
},
|
},
|
||||||
"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": "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 Клиент",
|
"new_customer": "New Клиент",
|
||||||
"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": "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} дней",
|
||||||
@ -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": "Загрузить",
|
||||||
"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,16 +397,16 @@
|
|||||||
"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",
|
"record_payment": "Добавить платёж",
|
||||||
"add_new_invoice": "Добавить новый счёт",
|
"add_new_invoice": "Добавить новый счёт",
|
||||||
"update_expense": "Обновить расходы",
|
"update_expense": "Обновить расходы",
|
||||||
"edit_invoice": "Редактировать счет-фактуру",
|
"edit_invoice": "Редактировать счет-фактуру",
|
||||||
@ -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": "Этот счет будет клонирован в новый счет",
|
||||||
@ -439,26 +439,26 @@
|
|||||||
"select_an_item": "Выберите товар",
|
"select_an_item": "Выберите товар",
|
||||||
"type_item_description": "Описание товара (необязательно)"
|
"type_item_description": "Описание товара (необязательно)"
|
||||||
},
|
},
|
||||||
"payment_attached_message": "One of the selected invoices already have a payment attached to it. Make sure to delete the attached payments first in order to go ahead with the removal",
|
"payment_attached_message": "К одному из выбранных счетов уже прикреплен платеж. Для удаления сначала удалите прикрепленные платежи",
|
||||||
"confirm_delete": "You will not be able to recover this Invoice | You will not be able to recover these Invoices",
|
"confirm_delete": "Восстановление данного счета будет невозможно | Восстановление данного счета будет невозможно",
|
||||||
"created_message": "Счет-фактура успешно создан",
|
"created_message": "Счет-фактура успешно создан",
|
||||||
"updated_message": "Счет-фактура успешно обновлен",
|
"updated_message": "Счет-фактура успешно обновлен",
|
||||||
"deleted_message": "Счет успешно удален | Счета успешно удалены",
|
"deleted_message": "Счет успешно удален | Счета успешно удалены",
|
||||||
"marked_as_sent_message": "Счет помечен как успешно отправленный",
|
"marked_as_sent_message": "Счет помечен как успешно отправленный",
|
||||||
"something_went_wrong": "что-то пошло не так",
|
"something_went_wrong": "что-то пошло не так",
|
||||||
"invalid_due_amount_message": "Total Invoice amount cannot be less than total paid amount for this Invoice. Please update the invoice or delete the associated payments to continue.",
|
"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": "Recurring Invoices",
|
||||||
"invoices_list": "Recurring Invoices List",
|
"invoices_list": "Recurring Invoices List",
|
||||||
"days": "{days} Days",
|
"days": "{days} Дней",
|
||||||
"months": "{months} Month",
|
"months": "{months} Месяц",
|
||||||
"years": "{years} Year",
|
"years": "{years} Год",
|
||||||
"all": "All",
|
"all": "Все",
|
||||||
"paid": "Paid",
|
"paid": "Оплачено",
|
||||||
"unpaid": "Unpaid",
|
"unpaid": "Не оплачено",
|
||||||
"viewed": "Viewed",
|
"viewed": "Просмотрено",
|
||||||
"overdue": "Просрочен",
|
"overdue": "Просрочен",
|
||||||
"active": "Активный",
|
"active": "Активный",
|
||||||
"completed": "Выполнен",
|
"completed": "Выполнен",
|
||||||
@ -466,14 +466,14 @@
|
|||||||
"paid_status": "СТАТУС ПЛАТЕЖА",
|
"paid_status": "СТАТУС ПЛАТЕЖА",
|
||||||
"ref_no": "REF NO.",
|
"ref_no": "REF NO.",
|
||||||
"number": "НОМЕР",
|
"number": "НОМЕР",
|
||||||
"amount_due": "AMOUNT DUE",
|
"amount_due": "К ОПЛАТЕ",
|
||||||
"partially_paid": "Partially Paid",
|
"partially_paid": "Частично оплачен",
|
||||||
"total": "Итого",
|
"total": "Итого",
|
||||||
"discount": "Скидка",
|
"discount": "Скидка",
|
||||||
"sub_total": "Промежуточный итог",
|
"sub_total": "Промежуточный итог",
|
||||||
"invoice": "Recurring Invoice | Recurring Invoices",
|
"invoice": "Recurring Invoice | Recurring Invoices",
|
||||||
"invoice_number": "Recurring Invoice Number",
|
"invoice_number": "Recurring Invoice Number",
|
||||||
"next_invoice_date": "Next Invoice Date",
|
"next_invoice_date": "Дата следующего счета",
|
||||||
"ref_number": "Ref Number",
|
"ref_number": "Ref Number",
|
||||||
"contact": "Контакты",
|
"contact": "Контакты",
|
||||||
"add_item": "Добавить элемент",
|
"add_item": "Добавить элемент",
|
||||||
@ -482,31 +482,31 @@
|
|||||||
"limit_date": "Limit Date",
|
"limit_date": "Limit Date",
|
||||||
"limit_count": "Limit Count",
|
"limit_count": "Limit Count",
|
||||||
"count": "Количество",
|
"count": "Количество",
|
||||||
"status": "Status",
|
"status": "Статус",
|
||||||
"select_a_status": "Выбрать статус",
|
"select_a_status": "Выбрать статус",
|
||||||
"working": "Working",
|
"working": "В процессе",
|
||||||
"on_hold": "На удержании",
|
"on_hold": "На удержании",
|
||||||
"complete": "Completed",
|
"complete": "Завершено",
|
||||||
"add_tax": "Add Tax",
|
"add_tax": "Добавить налог",
|
||||||
"amount": "Amount",
|
"amount": "Сумма",
|
||||||
"action": "Action",
|
"action": "Действие",
|
||||||
"notes": "Notes",
|
"notes": "Заметки",
|
||||||
"view": "View",
|
"view": "Просмотр",
|
||||||
"basic_info": "Basic Info",
|
"basic_info": "Общая информация",
|
||||||
"send_invoice": "Send Recurring Invoice",
|
"send_invoice": "Send Recurring Invoice",
|
||||||
"auto_send": "Auto Send",
|
"auto_send": "Автоотправка",
|
||||||
"resend_invoice": "Resend Recurring Invoice",
|
"resend_invoice": "Resend Recurring Invoice",
|
||||||
"invoice_template": "Recurring Invoice Template",
|
"invoice_template": "Recurring Invoice Template",
|
||||||
"conversion_message": "Recurring Invoice cloned successful",
|
"conversion_message": "Recurring Invoice cloned successful",
|
||||||
"template": "Шаблон",
|
"template": "Шаблон",
|
||||||
"mark_as_sent": "Mark as sent",
|
"mark_as_sent": "Пометить как отправленное",
|
||||||
"confirm_send_invoice": "This recurring invoice will be sent via email to the customer",
|
"confirm_send_invoice": "This recurring invoice will be sent via email to the customer",
|
||||||
"invoice_mark_as_sent": "This recurring invoice will be marked as sent",
|
"invoice_mark_as_sent": "This recurring invoice will be marked as sent",
|
||||||
"confirm_send": "This recurring invoice will be sent via email to the customer",
|
"confirm_send": "This recurring invoice will be sent via email to the customer",
|
||||||
"starts_at": "Start Date",
|
"starts_at": "Дата начала",
|
||||||
"due_date": "Invoice Due Date",
|
"due_date": "Срок оплаты счёта",
|
||||||
"record_payment": "Record Payment",
|
"record_payment": "Добавить платёж",
|
||||||
"add_new_invoice": "Add New Recurring Invoice",
|
"add_new_invoice": "Добавить новый повторяющийся счет",
|
||||||
"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",
|
||||||
@ -547,22 +547,22 @@
|
|||||||
"minute": "Minute",
|
"minute": "Minute",
|
||||||
"hour": "Hour",
|
"hour": "Hour",
|
||||||
"day_month": "Day of month",
|
"day_month": "Day of month",
|
||||||
"month": "Month",
|
"month": "Месяц",
|
||||||
"day_week": "Day of week"
|
"day_week": "День недели"
|
||||||
},
|
},
|
||||||
"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",
|
||||||
"updated_message": "Recurring Invoice updated successfully",
|
"updated_message": "Recurring Invoice updated successfully",
|
||||||
"deleted_message": "Recurring Invoice deleted successfully | Recurring Invoices deleted successfully",
|
"deleted_message": "Recurring Invoice deleted successfully | Recurring Invoices deleted successfully",
|
||||||
"marked_as_sent_message": "Recurring Invoice marked as sent successfully",
|
"marked_as_sent_message": "Recurring Invoice marked as sent successfully",
|
||||||
"user_email_does_not_exist": "User email does not exist",
|
"user_email_does_not_exist": "Адрес электронной почты пользователя не найден",
|
||||||
"something_went_wrong": "something went wrong",
|
"something_went_wrong": "что-то пошло не так",
|
||||||
"invalid_due_amount_message": "Total Recurring Invoice amount cannot be less than total paid amount for this Recurring Invoice. Please update the invoice or delete the associated payments to continue."
|
"invalid_due_amount_message": "Total Recurring Invoice amount cannot be less than total paid amount for this Recurring Invoice. Please update the invoice or delete the associated payments to continue."
|
||||||
},
|
},
|
||||||
"payments": {
|
"payments": {
|
||||||
"title": "Платежи",
|
"title": "Платежи",
|
||||||
"payments_list": "Список платежей",
|
"payments_list": "Список платежей",
|
||||||
"record_payment": "Record Payment",
|
"record_payment": "Добавить платёж",
|
||||||
"customer": "Клиент",
|
"customer": "Клиент",
|
||||||
"date": "Дата",
|
"date": "Дата",
|
||||||
"amount": "Сумма",
|
"amount": "Сумма",
|
||||||
@ -603,7 +603,7 @@
|
|||||||
"select_a_customer": "Выберите клиента",
|
"select_a_customer": "Выберите клиента",
|
||||||
"expense_title": "Заголовок",
|
"expense_title": "Заголовок",
|
||||||
"customer": "Клиент",
|
"customer": "Клиент",
|
||||||
"currency": "Currency",
|
"currency": "Валюта",
|
||||||
"contact": "Контакт",
|
"contact": "Контакт",
|
||||||
"category": "Категория",
|
"category": "Категория",
|
||||||
"from_date": "От даты",
|
"from_date": "От даты",
|
||||||
@ -626,11 +626,11 @@
|
|||||||
"new_expense": "Новый расход",
|
"new_expense": "Новый расход",
|
||||||
"expense": "Расход | Расходы",
|
"expense": "Расход | Расходы",
|
||||||
"no_expenses": "No expenses yet!",
|
"no_expenses": "No expenses yet!",
|
||||||
"list_of_expenses": "This section will contain the list of expenses.",
|
"list_of_expenses": "В этом разделе будет содержаться список расходов.",
|
||||||
"confirm_delete": "You will not be able to recover this Expense | You will not be able to recover these Expenses",
|
"confirm_delete": "You will not be able to recover this Expense | You will not be able to recover these Expenses",
|
||||||
"created_message": "Expense created successfully",
|
"created_message": "Расход создан успешно",
|
||||||
"updated_message": "Expense updated successfully",
|
"updated_message": "Расход успешно обновлен",
|
||||||
"deleted_message": "Expense deleted successfully | Expenses deleted successfully",
|
"deleted_message": "Расход успешно удален | Расходы успешно удалены",
|
||||||
"categories": {
|
"categories": {
|
||||||
"categories_list": "Список категорий",
|
"categories_list": "Список категорий",
|
||||||
"title": "Заголовок",
|
"title": "Заголовок",
|
||||||
@ -658,40 +658,40 @@
|
|||||||
"retype_password": "Повторите пароль"
|
"retype_password": "Повторите пароль"
|
||||||
},
|
},
|
||||||
"modules": {
|
"modules": {
|
||||||
"buy_now": "Buy Now",
|
"buy_now": "Купить",
|
||||||
"install": "Install",
|
"install": "Установить",
|
||||||
"price": "Price",
|
"price": "Цена",
|
||||||
"download_zip_file": "Download ZIP file",
|
"download_zip_file": "Скачать ZIP-файл",
|
||||||
"unzipping_package": "Unzipping Package",
|
"unzipping_package": "Распаковка пакета",
|
||||||
"copying_files": "Copying Files",
|
"copying_files": "Копирование файлов",
|
||||||
"deleting_files": "Deleting Unused files",
|
"deleting_files": "Удаление неиспользуемых файлов",
|
||||||
"completing_installation": "Completing Installation",
|
"completing_installation": "Завершение установки",
|
||||||
"update_failed": "Update Failed",
|
"update_failed": "Не удалось обновить",
|
||||||
"install_success": "Module has been installed successfully!",
|
"install_success": "Модуль успешно установлен!",
|
||||||
"customer_reviews": "Reviews",
|
"customer_reviews": "Отзывы",
|
||||||
"license": "License",
|
"license": "Лицензия",
|
||||||
"faq": "FAQ",
|
"faq": "Часто задаваемые вопросы",
|
||||||
"monthly": "Monthly",
|
"monthly": "Ежемесячно",
|
||||||
"yearly": "Yearly",
|
"yearly": "Ежегодно",
|
||||||
"updated": "Updated",
|
"updated": "Обновлено",
|
||||||
"version": "Version",
|
"version": "Версия",
|
||||||
"disable": "Disable",
|
"disable": "Отключить",
|
||||||
"module_disabled": "Module Disabled",
|
"module_disabled": "Модуль отключен",
|
||||||
"enable": "Enable",
|
"enable": "Включить",
|
||||||
"module_enabled": "Module Enabled",
|
"module_enabled": "Модуль включен",
|
||||||
"update_to": "Update To",
|
"update_to": "Обновить до",
|
||||||
"module_updated": "Module Updated Successfully!",
|
"module_updated": "Модуль успешно обновлен!",
|
||||||
"title": "Modules",
|
"title": "Модули",
|
||||||
"module": "Module | Modules",
|
"module": "Модуль | Модули",
|
||||||
"api_token": "API token",
|
"api_token": "API-токен",
|
||||||
"invalid_api_token": "Invalid API Token.",
|
"invalid_api_token": "Неверный API-токен.",
|
||||||
"other_modules": "Other Modules",
|
"other_modules": "Другие модули",
|
||||||
"view_all": "Показать всё",
|
"view_all": "Показать всё",
|
||||||
"no_reviews_found": "There are no reviews for this module yet!",
|
"no_reviews_found": "Для данного модуля пока нет отзывов!",
|
||||||
"module_not_purchased": "Module Not Purchased",
|
"module_not_purchased": "Модуль не приобретен",
|
||||||
"module_not_found": "Module Not Found",
|
"module_not_found": "Модуль не найден",
|
||||||
"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": "Последнее обновление",
|
||||||
"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",
|
||||||
@ -911,49 +911,49 @@
|
|||||||
"sort_in_alphabetical_order": "Sort in Alphabetical Order",
|
"sort_in_alphabetical_order": "Sort in Alphabetical Order",
|
||||||
"add_options_in_bulk": "Add options in bulk",
|
"add_options_in_bulk": "Add options in bulk",
|
||||||
"use_predefined_options": "Use Predefined Options",
|
"use_predefined_options": "Use Predefined Options",
|
||||||
"select_custom_date": "Select Custom Date",
|
"select_custom_date": "Выберите произвольную дату",
|
||||||
"select_relative_date": "Select Relative Date",
|
"select_relative_date": "Выберите относительную дату",
|
||||||
"ticked_by_default": "Ticked by default",
|
"ticked_by_default": "Отмечен по умолчанию",
|
||||||
"updated_message": "Custom Field updated successfully",
|
"updated_message": "Пользовательское поле успешно обновлено",
|
||||||
"added_message": "Custom Field added successfully",
|
"added_message": "Пользовательское поле успешно добавлено",
|
||||||
"press_enter_to_add": "Press enter to add new option",
|
"press_enter_to_add": "Нажмите ввод для добавления новой опции",
|
||||||
"model_in_use": "Cannot update model for fields which are already in use.",
|
"model_in_use": "Невозможно обновить модель для полей, которые уже используются.",
|
||||||
"type_in_use": "Cannot update type for fields which are already in use."
|
"type_in_use": "Невозможно обновить тип для полей, которые уже используются."
|
||||||
},
|
},
|
||||||
"customization": {
|
"customization": {
|
||||||
"customization": "customization",
|
"customization": "персонализация",
|
||||||
"updated_message": "Информация о компании успешно обновлена",
|
"updated_message": "Информация о компании успешно обновлена",
|
||||||
"save": "Сохранить",
|
"save": "Сохранить",
|
||||||
"insert_fields": "Insert Fields",
|
"insert_fields": "Вставить поля",
|
||||||
"learn_custom_format": "Learn how to use custom format",
|
"learn_custom_format": "Learn how to use custom format",
|
||||||
"add_new_component": "Add New Component",
|
"add_new_component": "Добавить компонент",
|
||||||
"component": "Component",
|
"component": "Компонент",
|
||||||
"Parameter": "Parameter",
|
"Parameter": "Параметр",
|
||||||
"series": "Series",
|
"series": "Series",
|
||||||
"series_description": "To set a static prefix/postfix like 'INV' across your company. It supports character length of up to 6 chars.",
|
"series_description": "To set a static prefix/postfix like 'INV' across your company. It supports character length of up to 6 chars.",
|
||||||
"series_param_label": "Series Value",
|
"series_param_label": "Series Value",
|
||||||
"delimiter": "Delimiter",
|
"delimiter": "Разделитель",
|
||||||
"delimiter_description": "Single character for specifying the boundary between 2 separate components. By default its set to -",
|
"delimiter_description": "Символ для обозначения границы между двумя компонентами. По умолчанию имеет значение -",
|
||||||
"delimiter_param_label": "Delimiter Value",
|
"delimiter_param_label": "Разделитель",
|
||||||
"date_format": "Date Format",
|
"date_format": "Формат даты",
|
||||||
"date_format_description": "A local date and time field which accepts a format parameter. The default format: 'Y' renders the current year.",
|
"date_format_description": "A local date and time field which accepts a format parameter. The default format: 'Y' renders the current year.",
|
||||||
"date_format_param_label": "Format",
|
"date_format_param_label": "Формат",
|
||||||
"sequence": "Sequence",
|
"sequence": "Последовательность",
|
||||||
"sequence_description": "Consecutive sequence of numbers across your company. You can specify the length on the given parameter.",
|
"sequence_description": "Consecutive sequence of numbers across your company. You can specify the length on the given parameter.",
|
||||||
"sequence_param_label": "Sequence Length",
|
"sequence_param_label": "Длина последовательности",
|
||||||
"customer_series": "Customer Series",
|
"customer_series": "Customer Series",
|
||||||
"customer_series_description": "To set a different prefix/postfix for each customer.",
|
"customer_series_description": "Установить отдельный префикс/постфикс для каждого клиента.",
|
||||||
"customer_sequence": "Customer Sequence",
|
"customer_sequence": "Customer Sequence",
|
||||||
"customer_sequence_description": "Consecutive sequence of numbers for each of your customer.",
|
"customer_sequence_description": "Consecutive sequence of numbers for each of your customer.",
|
||||||
"customer_sequence_param_label": "Sequence Length",
|
"customer_sequence_param_label": "Длина последовательности",
|
||||||
"random_sequence": "Random Sequence",
|
"random_sequence": "Случайная последовательность",
|
||||||
"random_sequence_description": "Random alphanumeric string. You can specify the length on the given parameter.",
|
"random_sequence_description": "Произвольная буквенно-цифровая строка. Вы можете указать длину в качестве параметра.",
|
||||||
"random_sequence_param_label": "Sequence Length",
|
"random_sequence_param_label": "Длина последовательности",
|
||||||
"invoices": {
|
"invoices": {
|
||||||
"title": "Счет-фактуры",
|
"title": "Счет-фактуры",
|
||||||
"invoice_number_format": "Invoice Number Format",
|
"invoice_number_format": "Формат номера счета",
|
||||||
"invoice_number_format_description": "Customize how your invoice number gets generated automatically when you create a new invoice.",
|
"invoice_number_format_description": "Customize how your invoice number gets generated automatically when you create a new invoice.",
|
||||||
"preview_invoice_number": "Preview Invoice Number",
|
"preview_invoice_number": "Предосмотр номера счета",
|
||||||
"due_date": "Due Date",
|
"due_date": "Due Date",
|
||||||
"due_date_description": "Specify how due date is automatically set when you create an invoice.",
|
"due_date_description": "Specify how due date is automatically set when you create an invoice.",
|
||||||
"due_date_days": "Invoice Due after days",
|
"due_date_days": "Invoice Due after days",
|
||||||
@ -969,7 +969,7 @@
|
|||||||
"invoice_email_attachment_setting_description": "Включите, если вы хотите отправлять счета-фактуры как вложение по электронной почте. Пожалуйста, обратите внимание, что кнопка «Просмотр счета» в письмах больше не будет отображаться, если включено.",
|
"invoice_email_attachment_setting_description": "Включите, если вы хотите отправлять счета-фактуры как вложение по электронной почте. Пожалуйста, обратите внимание, что кнопка «Просмотр счета» в письмах больше не будет отображаться, если включено.",
|
||||||
"invoice_settings_updated": "Invoice Settings updated successfully",
|
"invoice_settings_updated": "Invoice Settings updated successfully",
|
||||||
"retrospective_edits": "Retrospective Edits",
|
"retrospective_edits": "Retrospective Edits",
|
||||||
"allow": "Allow",
|
"allow": "Разрешить",
|
||||||
"disable_on_invoice_partial_paid": "Disable after partial payment is recorded",
|
"disable_on_invoice_partial_paid": "Disable after partial payment is recorded",
|
||||||
"disable_on_invoice_paid": "Disable after full payment is recorded",
|
"disable_on_invoice_paid": "Disable after full payment is recorded",
|
||||||
"disable_on_invoice_sent": "Disable after invoice is sent",
|
"disable_on_invoice_sent": "Disable after invoice is sent",
|
||||||
|
|||||||
1525
resources/scripts/locales/th.json
Normal file
1525
resources/scripts/locales/th.json
Normal file
File diff suppressed because it is too large
Load Diff
BIN
resources/static/fonts/THSarabunNew-Bold.ttf
Normal file
BIN
resources/static/fonts/THSarabunNew-Bold.ttf
Normal file
Binary file not shown.
BIN
resources/static/fonts/THSarabunNew-BoldItalic.ttf
Normal file
BIN
resources/static/fonts/THSarabunNew-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
resources/static/fonts/THSarabunNew-Italic.ttf
Normal file
BIN
resources/static/fonts/THSarabunNew-Italic.ttf
Normal file
Binary file not shown.
BIN
resources/static/fonts/THSarabunNew.ttf
Normal file
BIN
resources/static/fonts/THSarabunNew.ttf
Normal file
Binary file not shown.
@ -386,6 +386,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@if (App::isLocale('th'))
|
||||||
|
@include('app.pdf.locale.th')
|
||||||
|
@endif
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@ -408,6 +408,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@if (App::isLocale('th'))
|
||||||
|
@include('app.pdf.locale.th')
|
||||||
|
@endif
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@ -346,6 +346,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@if (App::isLocale('th'))
|
||||||
|
@include('app.pdf.locale.th')
|
||||||
|
@endif
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@ -327,6 +327,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@if (App::isLocale('th'))
|
||||||
|
@include('app.pdf.locale.th')
|
||||||
|
@endif
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@ -377,6 +377,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@if (App::isLocale('th'))
|
||||||
|
@include('app.pdf.locale.th')
|
||||||
|
@endif
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<title>@lang('pdf_invoice_label') - {{$invoice->invoice_number}}</title>
|
<title>@lang('pdf_invoice_label') - {{ $invoice->invoice_number }}</title>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
|
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
@ -187,7 +187,7 @@
|
|||||||
|
|
||||||
|
|
||||||
.total-display-table {
|
.total-display-table {
|
||||||
border-top: none;
|
border-top: none;
|
||||||
page-break-inside: avoid;
|
page-break-inside: avoid;
|
||||||
page-break-before: auto;
|
page-break-before: auto;
|
||||||
page-break-after: auto;
|
page-break-after: auto;
|
||||||
@ -304,7 +304,12 @@
|
|||||||
.pl-0 {
|
.pl-0 {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@if (App::isLocale('th'))
|
||||||
|
@include('app.pdf.locale.th')
|
||||||
|
@endif
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
@ -312,10 +317,10 @@
|
|||||||
<table width="100%">
|
<table width="100%">
|
||||||
<tr>
|
<tr>
|
||||||
<td width="50%" class="header-section-left">
|
<td width="50%" class="header-section-left">
|
||||||
@if($logo)
|
@if ($logo)
|
||||||
<img class="header-logo" style="height: 50px;" src="{{ $logo }}" alt="Company Logo">
|
<img class="header-logo" style="height: 50px;" src="{{ $logo }}" alt="Company Logo">
|
||||||
@else
|
@else
|
||||||
<h1 class="header-logo"> {{$invoice->customer->company->name}} </h1>
|
<h1 class="header-logo"> {{ $invoice->customer->company->name }} </h1>
|
||||||
@endif
|
@endif
|
||||||
</td>
|
</td>
|
||||||
<td width="50%" class="text-right company-address-container company-address">
|
<td width="50%" class="text-right company-address-container company-address">
|
||||||
@ -331,14 +336,14 @@
|
|||||||
<div class="main-content">
|
<div class="main-content">
|
||||||
<div class="customer-address-container">
|
<div class="customer-address-container">
|
||||||
<div class="billing-address-container billing-address">
|
<div class="billing-address-container billing-address">
|
||||||
@if($billing_address)
|
@if ($billing_address)
|
||||||
<b>@lang('pdf_bill_to')</b> <br>
|
<b>@lang('pdf_bill_to')</b> <br>
|
||||||
{!! $billing_address !!}
|
{!! $billing_address !!}
|
||||||
@endif
|
@endif
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div @if($billing_address !== '</br>') class="shipping-address-container shipping-address" @else class="shipping-address-container--left shipping-address" @endif>
|
<div @if ($billing_address !== '</br>') class="shipping-address-container shipping-address" @else class="shipping-address-container--left shipping-address" @endif>
|
||||||
@if($shipping_address)
|
@if ($shipping_address)
|
||||||
<b>@lang('pdf_ship_to')</b> <br>
|
<b>@lang('pdf_ship_to')</b> <br>
|
||||||
{!! $shipping_address !!}
|
{!! $shipping_address !!}
|
||||||
@endif
|
@endif
|
||||||
@ -350,15 +355,15 @@
|
|||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="attribute-label">@lang('pdf_invoice_number')</td>
|
<td class="attribute-label">@lang('pdf_invoice_number')</td>
|
||||||
<td class="attribute-value"> {{$invoice->invoice_number}}</td>
|
<td class="attribute-value"> {{ $invoice->invoice_number }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="attribute-label">@lang('pdf_invoice_date')</td>
|
<td class="attribute-label">@lang('pdf_invoice_date')</td>
|
||||||
<td class="attribute-value"> {{$invoice->formattedInvoiceDate}}</td>
|
<td class="attribute-value"> {{ $invoice->formattedInvoiceDate }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="attribute-label">@lang('pdf_invoice_due_date')</td>
|
<td class="attribute-label">@lang('pdf_invoice_due_date')</td>
|
||||||
<td class="attribute-value"> {{$invoice->formattedDueDate}}</td>
|
<td class="attribute-value"> {{ $invoice->formattedDueDate }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@ -368,7 +373,7 @@
|
|||||||
@include('app.pdf.invoice.partials.table')
|
@include('app.pdf.invoice.partials.table')
|
||||||
|
|
||||||
<div class="notes">
|
<div class="notes">
|
||||||
@if($notes)
|
@if ($notes)
|
||||||
<div class="notes-label">
|
<div class="notes-label">
|
||||||
@lang('pdf_notes')
|
@lang('pdf_notes')
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
34
resources/views/app/pdf/locale/th.blade.php
Normal file
34
resources/views/app/pdf/locale/th.blade.php
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<style type="text/css">
|
||||||
|
@font-face {
|
||||||
|
font-family: 'THSarabunNew';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
src: url("{{ resource_path('static/fonts/THSarabunNew.ttf') }}") format('truetype');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'THSarabunNew';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: bold;
|
||||||
|
src: url("{{ resource_path('static/fonts/THSarabunNew-Bold.ttf') }}") format('truetype');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'THSarabunNew';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: normal;
|
||||||
|
src: url("{{ resource_path('static/fonts/THSarabunNew-Italic.ttf') }}") format('truetype');
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: 'THSarabunNew';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: bold;
|
||||||
|
src: url("{{ resource_path('static/fonts/THSarabunNew-BoldItalic.ttf') }}") format('truetype');
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: "THSarabunNew", sans-serif !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user