progress in view invoice sell

This commit is contained in:
Hesabix 2025-03-29 13:03:41 +00:00
parent 474a1eff10
commit 6d63f427ac
5 changed files with 920 additions and 841 deletions

View file

@ -0,0 +1,134 @@
<script lang="ts">
import { defineComponent } from 'vue';
import axios from 'axios';
export default defineComponent({
name: 'PrintOptions',
props: {
invoiceId: { type: String, required: true },
},
data: () => ({
dialog: false,
loading: false,
printOptions: {
pays: true,
note: true,
bidInfo: true,
taxInfo: true,
discountInfo: true,
paper: 'A4-L',
},
}),
methods: {
printInvoice(pdf = true, cloudPrinters = true) {
this.loading = true;
axios
.post('/api/sell/print/invoice', {
code: this.$props.invoiceId,
pdf,
printers: cloudPrinters,
printOptions: this.printOptions,
})
.then((response) => {
window.open(`${this.$API_URL}/front/print/${response.data.id}`, '_blank', 'noreferrer');
this.dialog = false;
})
.catch((error) => {
console.error('خطا در چاپ:', error);
})
.finally(() => {
this.loading = false;
});
},
// متد برای جلوگیری از بسته شدن دیالوگ با کلیک خارج
preventClose() {
// هیچ عملی انجام نمیشود تا دیالوگ بسته نشود
},
},
});
</script>
<template>
<v-btn icon color="primary" class="ml-2" @click="dialog = true">
<v-icon>mdi-printer</v-icon>
<v-tooltip activator="parent" location="bottom">چاپ فاکتور</v-tooltip>
</v-btn>
<v-dialog
v-model="dialog"
max-width="500"
persistent
@click:outside="preventClose"
>
<v-card>
<v-toolbar color="grey-lighten-4" flat>
<v-toolbar-title>
<v-icon color="primary" left>mdi-printer</v-icon>
چاپ فاکتور
</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn
icon
color="green"
:loading="loading"
@click="printInvoice"
class="mx-1"
>
<v-icon>mdi-printer</v-icon>
<v-tooltip activator="parent" location="bottom">چاپ فاکتور</v-tooltip>
</v-btn>
<v-btn icon @click="dialog = false" :disabled="loading">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-toolbar>
<v-card-text class="pt-4">
<p class="mb-2">برای تغییر تنظیمات پیشفرض به بخش تنظیمات چاپ مراجعه نمایید</p>
<v-select
v-model="printOptions.paper"
:items="[
{ title: 'A4 افقی', value: 'A4-L' },
{ title: 'A4 عمودی', value: 'A4' },
{ title: 'A5 افقی', value: 'A5-L' },
{ title: 'A5 عمودی', value: 'A5' },
]"
label="سایز کاغذ و حالت چاپ"
variant="outlined"
></v-select>
<v-switch
v-model="printOptions.bidInfo"
label="اطلاعات کسب‌وکار"
class="my-1"
hide-details
></v-switch>
<v-switch
v-model="printOptions.pays"
label="نمایش پرداخت‌های فاکتور"
class="my-1"
hide-details
></v-switch>
<v-switch
v-model="printOptions.note"
label="یادداشت پایین فاکتور"
class="my-1"
hide-details
></v-switch>
<v-switch
v-model="printOptions.taxInfo"
label="مالیات به تفکیک اقلام"
class="my-1"
hide-details
></v-switch>
<v-switch
v-model="printOptions.discountInfo"
label="تخفیف به تفکیک اقلام"
class="my-1"
hide-details
></v-switch>
</v-card-text>
</v-card>
</v-dialog>
</template>
<style scoped>
/* استایل‌های اضافی در صورت نیاز */
</style>

View file

@ -0,0 +1,157 @@
<script lang="ts">
import { defineComponent, ref } from 'vue';
import axios from 'axios';
export default defineComponent({
name: 'ShareOptions',
props: {
shortlinkUrl: { type: String, required: true },
mobile: { type: String, required: true },
invoiceId: { type: Number, required: true },
},
data: () => ({
dialog: false,
internalMobile: '',
copyLabel: ref('کپی'),
messageHint: ref('ارسال'),
hintColor: ref('grey'),
loading: ref(false),
}),
created() {
this.internalMobile = this.mobile;
},
methods: {
copyToClipboard() {
navigator.clipboard.writeText(this.shortlinkUrl);
this.copyLabel = 'کپی شد!';
},
sendSMS() {
this.loading = true;
const regex = new RegExp('^(\\+98|0)?9\\d{9}$');
if (!regex.test(this.internalMobile)) {
this.messageHint = 'شماره موبایل نامعتبر است.';
this.hintColor = 'red';
this.loading = false;
return;
}
this.messageHint = 'در حال ارسال...';
this.hintColor = 'grey';
axios
.post(`/api/sms/send/sell-invoice/${this.invoiceId}/${this.internalMobile}`)
.then((response) => {
if (response.data.result == 2) {
this.messageHint = 'اعتبار سرویس پیامک کافی نیست.';
this.hintColor = 'red';
} else if (response.data.result == 1) {
this.messageHint = 'پیامک ارسال شد!';
this.hintColor = 'green';
}
this.loading = false;
})
.catch((error) => {
console.error('خطا در ارسال پیامک:', error);
this.messageHint = 'خطا در ارسال پیامک رخ داد.';
this.hintColor = 'red';
this.loading = false;
});
},
},
});
</script>
<template>
<v-btn icon color="success" class="ml-2" @click="dialog = true">
<v-icon>mdi-share-variant</v-icon>
<v-tooltip activator="parent" location="bottom">اشتراکگذاری</v-tooltip>
</v-btn>
<v-dialog v-model="dialog" max-width="500" persistent>
<v-card>
<v-toolbar color="grey-lighten-4" flat>
<v-toolbar-title>
<v-icon color="success" left>mdi-share-variant</v-icon>
اشتراکگذاری
</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn icon @click="dialog = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-toolbar>
<v-card-text>
<v-text-field
v-model="shortlinkUrl"
readonly
append-inner-icon="mdi-content-copy"
@click:append-inner="copyToClipboard"
label="پیوند فاکتور"
variant="outlined"
:hint="copyLabel"
></v-text-field>
<v-text-field
class="mt-4"
v-model="internalMobile"
label="ارسال پیامک"
variant="outlined"
:hint="messageHint"
:color="hintColor"
persistent-hint
>
<template v-slot:append-inner>
<v-btn
color="primary"
:loading="loading"
@click="sendSMS"
>
ارسال
</v-btn>
</template>
</v-text-field>
<div class="mt-3">
<v-icon left>mdi-share-variant</v-icon>
اشتراکگذاری در شبکههای اجتماعی
<div class="mt-2">
<!-- تلگرام -->
<a
:href="'https://t.me/share/url?url=' + encodeURIComponent(shortlinkUrl)"
target="_blank"
>
<img src="/img/icons/telegram.png" class="m-3" style="max-width: 30px;" />
</a>
<!-- ایتا -->
<a
:href="'https://eitaa.com/?text=' + encodeURIComponent(shortlinkUrl)"
target="_blank"
>
<img src="/img/icons/eitaa.jpeg" class="m-3" style="max-width: 30px;" />
</a>
<!-- بله -->
<a
:href="'https://ble.ir/share/url?url=' + encodeURIComponent(shortlinkUrl)"
target="_blank"
>
<img src="/img/icons/bale-logo.png" class="m-3" style="max-width: 30px;" />
</a>
<!-- روبیکا -->
<a
:href="'https://rubika.ir/app/share?text=' + encodeURIComponent(shortlinkUrl)"
target="_blank"
>
<img src="/img/icons/robika.png" class="m-3" style="max-width: 30px;" />
</a>
<!-- واتساپ -->
<a
:href="'https://api.whatsapp.com/send?text=' + encodeURIComponent(shortlinkUrl)"
target="_blank"
>
<v-icon class="m-3" size="30" color="green">mdi-whatsapp</v-icon>
</a>
</div>
</div>
</v-card-text>
</v-card>
</v-dialog>
</template>
<style scoped>
/* استایل‌های اضافی در صورت نیاز */
</style>

View file

@ -1,210 +1,229 @@
<script lang="ts">
import {defineComponent} from 'vue'
import axios from "axios";
import Swal from "sweetalert2";
import { defineComponent } from 'vue';
import axios from 'axios';
import Swal from 'sweetalert2';
export default defineComponent({
name: "archiveUpload",
props:{
name: 'archiveUpload',
props: {
doctype: String,
docid: [String ,Number],
cat: String
docid: [String, Number],
cat: String,
},
data() {
return {
fileStack:[],
des:'',
dialog: false, // برای باز و بسته کردن دیالوگ
fileStack: [],
des: '',
media: {
saved: [],
added: [],
removed: []
}
}
removed: [],
},
};
},
mounted() {
this.getFilesList();
},
methods:{
changeMedia(media){
this.media = media
methods: {
changeMedia(media) {
this.media = media;
},
addMedia(addedImage, addedMedia){
this.media.added = addedMedia
addMedia(addedImage, addedMedia) {
this.media.added = addedMedia;
},
removeMedia(removedImage, removedMedia){
this.media.removed = removedMedia
removeMedia(removedImage, removedMedia) {
this.media.removed = removedMedia;
},
getFilesList(){
axios.post('api/archive/files/list',{
id:this.$props.docid,
type:this.$props.doctype
}).then((resp)=>{
this.media.added =[];
getFilesList() {
axios.post('/api/archive/files/list', {
id: this.$props.docid,
type: this.$props.doctype,
}).then((resp) => {
this.media.added = [];
this.fileStack = resp.data;
this.fileStack.forEach((item)=>{
axios.post(this.$filters.getApiUrl() + '/api/archive/file/get/' + item.id, { responseType: "arraybuffer" })
.then((response)=>{
const b64 = btoa(String.fromCharCode(...new Uint8Array(response.data)));
item.fileBin = "data:" + response.headers['content-type'] + ";base64," + b64;
});
})
})
this.fileStack.forEach((item) => {
axios
.post(`${this.$filters.getApiUrl()}/api/archive/file/get/${item.id}`, { responseType: 'arraybuffer' })
.then((response) => {
const b64 = btoa(String.fromCharCode(...new Uint8Array(response.data)));
item.fileBin = `data:${response.headers['content-type']};base64,${b64}`;
});
});
});
},
deleteItem(item){
deleteItem(item) {
Swal.fire({
text: 'آیا برای حذف فایل مطمئن هستید؟',
showCancelButton: true,
confirmButtonText: 'بله',
cancelButtonText: `خیر`,
cancelButtonText: 'خیر',
}).then((result) => {
/* Read more about isConfirmed, isDenied below */
if (result.isConfirmed) {
axios.post('api/archive/file/remove/' + item.id).then((response)=>{
if(response.data.result == 1){
axios.post(`/api/archive/file/remove/${item.id}`).then((response) => {
if (response.data.result === 1) {
this.getFilesList();
Swal.fire({
text: 'فایل با موفقیت حذف شد.',
icon: 'success',
confirmButtonText: 'قبول'
confirmButtonText: 'قبول',
});
}
})
}
})
},
downloadFile(item){
axios.post(this.$filters.getApiUrl() + '/api/archive/file/get/' + item.id, { responseType: "arraybuffer" })
.then(response => {
const blob = new Blob([response.data], {type: item.fileType});
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = item.filename;
link.click();
URL.revokeObjectURL(link.href);
});
},
submitArchive(){
let formData = new FormData(document.getElementById('archive-file-upload'))
axios.post('api/archive/file/save',formData,{
headers: {
'Content-Type': 'multipart/form-data'
}
}).then((resp)=>{
if(resp.data.result === 'nem'){
Swal.fire({
text: 'فضای کافی وجود ندارد لطفا حساب کاربری خود را شارژ نمایید.',
icon: 'success',
confirmButtonText: 'قبول'
});
}else{
Swal.fire({
text: 'فایل‌های انتخابی ذخیره شدند.',
icon: 'success',
confirmButtonText: 'قبول'
});
this.getFilesList();
}
});
}
},
downloadFile(item) {
axios
.post(`${this.$filters.getApiUrl()}/api/archive/file/get/${item.id}`, { responseType: 'arraybuffer' })
.then((response) => {
const blob = new Blob([response.data], { type: item.fileType });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = item.filename;
link.click();
URL.revokeObjectURL(link.href);
});
},
submitArchive() {
const formData = new FormData(document.getElementById('archive-file-upload'));
axios
.post('/api/archive/file/save', formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
})
.then((resp) => {
if (resp.data.result === 'nem') {
Swal.fire({
text: 'فضای کافی وجود ندارد لطفا حساب کاربری خود را شارژ نمایید.',
icon: 'error',
confirmButtonText: 'قبول',
});
} else {
Swal.fire({
text: 'فایل‌های انتخابی ذخیره شدند.',
icon: 'success',
confirmButtonText: 'قبول',
});
this.getFilesList();
this.des = ''; // ریست کردن توضیحات
}
})
.catch((error) => {
console.error('خطا در آپلود:', error);
Swal.fire({
text: 'خطایی در بارگذاری فایل رخ داد.',
icon: 'error',
confirmButtonText: 'قبول',
});
});
},
},
})
});
</script>
<template>
<!-- Button trigger modal -->
<button type="button" class="btn btn-sm btn-outline-success mx-2" data-bs-toggle="modal" data-bs-target="#archiveModal">
<span class="badge text-bg-dark me-2">{{fileStack.length}}</span>
<i class="fa fa-file me-1"></i>
<span class="d-none d-sm-inline-block">آرشیو</span>
</button>
<!-- Modal -->
<div class="modal modal-lg fade" data-bs-backdrop="static" data-bs-keyboard="false" id="archiveModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabel">آرشیو فایل</h1>
<div class="block-options">
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
</div>
<div class="modal-body">
<h5 class="text-primary-dark mt-3 my-0 py-0">افزودن فایل جدید</h5>
<div>
<form id="archive-file-upload" @submit.prevent="submitArchive()">
<input type="hidden" name="doctype" :value="$props.doctype">
<input type="hidden" name="docid" :value="$props.docid">
<input type="hidden" name="cat" :value="$props.cat">
<Uploader
:server="$filters.getApiUrl() + '/api/archive/file/upload'"
:media="media.saved"
path="/storage/media"
@add="addMedia"
@remove="removeMedia"
@change="changeMedia"
:maxFilesize="5"
/>
<div class="container-fluid mt-2">
<div class="row">
<div class="col-9">
<input class="form-control" type="text" name="des" v-model="des" placeholder="توضیحات" aria-label="توضیحات">
</div>
<div class="col-3">
<button type="submit" class="btn btn-success d-flex">
<i class="fa fa-save me-2"></i>
بارگذاری فایلها
</button>
</div>
</div>
</div>
<!-- دکمه در تولبار -->
<v-btn icon color="success" class="ml-2" @click="dialog = true">
<v-badge :content="fileStack.length.toString()" color="dark">
<v-icon>mdi-archive</v-icon>
</v-badge>
<v-tooltip activator="parent" location="bottom">آرشیو</v-tooltip>
</v-btn>
<!-- دیالوگ آرشیو -->
<v-dialog v-model="dialog" max-width="800" persistent>
<v-card>
<v-toolbar color="grey-lighten-4" flat>
<v-toolbar-title>
<v-icon color="success" left>mdi-archive</v-icon>
آرشیو فایل
</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn icon @click="dialog = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-toolbar>
<v-card-text>
<!-- بخش افزودن فایل جدید -->
<v-row>
<v-col cols="12">
<form id="archive-file-upload" @submit.prevent="submitArchive">
<input type="hidden" name="doctype" :value="$props.doctype" />
<input type="hidden" name="docid" :value="$props.docid" />
<input type="hidden" name="cat" :value="$props.cat" />
<Uploader
:server="$filters.getApiUrl() + '/api/archive/file/upload'"
:media="media.saved"
path="/storage/media"
@add="addMedia"
@remove="removeMedia"
@change="changeMedia"
:maxFilesize="5"
/>
<v-text-field
v-model="des"
label="توضیحات"
placeholder="توضیحات"
outlined
name="des"
class="mt-2"
></v-text-field>
<v-btn type="submit" color="success" class="mt-2">
<v-icon left>mdi-content-save</v-icon>
بارگذاری فایلها
</v-btn>
</form>
</div>
<hr>
<h5 class="text-primary-dark mt-3 mb-0 pb-0">آرشیو فایلها</h5>
<table class="table table-striped table-hover table-borderless table-vcenter fs-sm text-center">
<thead>
<tr class="text-uppercase">
<th>پیش نمایش</th>
<th>نام فایل</th>
<th class="">سایز فایل(مگابایت)</th>
<th class="">تاریخ</th>
<th>عملیات</th>
</tr>
</thead>
<tbody>
<tr v-for="item in fileStack">
<td>
<img class="img-fluid" :src="item.fileBin" alt="پیش نمایش">
</td>
<td>
<span class="fw-semibold">{{item.filename}}</span>
</td>
<td>
<span>{{item.filesize}}</span>
</td>
<td>
<span>{{item.dateSubmit}}</span>
</td>
<td class="text-center text-nowrap fw-medium">
<a class="btn btn-sm btn-link" href="/" @click.prevent="downloadFile(item)">
<i class="fa fa-download"></i>
</a>
<button @click="deleteItem(item)" class="btn btn-sm ms-2 btn-link text-danger">
<i class="fa fa-trash"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">بازگشت</button>
</div>
</div>
</div>
</div>
</v-col>
</v-row>
<v-divider class="my-4"></v-divider>
<!-- لیست فایلها -->
<v-row>
<v-col cols="12">
<h5 class="text-primary">آرشیو فایلها</h5>
<v-table class="elevation-1">
<thead>
<tr>
<th class="text-center">پیشنمایش</th>
<th class="text-center">نام فایل</th>
<th class="text-center">سایز فایل (مگابایت)</th>
<th class="text-center">تاریخ</th>
<th class="text-center">عملیات</th>
</tr>
</thead>
<tbody>
<tr v-for="item in fileStack" :key="item.id">
<td class="text-center">
<v-img :src="item.fileBin" max-width="50" max-height="50" class="mx-auto" alt="پیش‌نمایش" />
</td>
<td class="text-center">{{ item.filename }}</td>
<td class="text-center">{{ item.filesize }}</td>
<td class="text-center">{{ item.dateSubmit }}</td>
<td class="text-center">
<v-btn icon small color="primary" @click="downloadFile(item)">
<v-icon>mdi-download</v-icon>
</v-btn>
<v-btn icon small color="error" class="ml-2" @click="deleteItem(item)">
<v-icon>mdi-trash-can</v-icon>
</v-btn>
</td>
</tr>
<tr v-if="fileStack.length === 0">
<td colspan="5" class="text-center text-muted">فایلی موجود نیست</td>
</tr>
</tbody>
</v-table>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-dialog>
</template>
<style scoped>
/* استایل‌های اضافی اگه نیاز باشه */
</style>

View file

@ -1,127 +1,210 @@
<script lang="ts">
import { defineComponent } from 'vue';
import axios from 'axios';
import { defineComponent } from 'vue'
import Swal from "sweetalert2";
import { ref } from 'vue';
import Loading from 'vue-loading-overlay';
export default defineComponent({
name: "notes",
components:{
Loading
},
name: 'Notes',
props: {
stat: Object,
code: String,
typeNote: String
typeNote: String,
},
data: () => {
data() {
return {
loading: ref(true),
dialog: false, // برای باز و بسته کردن دیالوگ
loading: false,
items: [],
des: '',
}
snackbar: false, // برای نمایش اسنکبار
snackbarText: '', // متن پیام اسنکبار
snackbarColor: 'success', // رنگ اسنکبار (success, error)
};
},
mounted() {
this.loadData();
},
methods: {
loadData() {
this.loading = true;
axios.post('/api/notes/list', {
type: this.$props.typeNote,
code: this.$props.code
}).then((response) => {
this.items = response.data;
this.$props.stat.count = response.data.length;
this.loading = false;
});
axios
.post('/api/notes/list', {
type: this.$props.typeNote,
code: this.$props.code,
})
.then((response) => {
this.items = response.data;
this.$props.stat.count = response.data.length;
this.loading = false;
});
},
remove(id) {
this.loading = true;
axios.post('/api/notes/remove/' + id).then((response) => {
axios.post(`/api/notes/remove/${id}`).then((response) => {
this.loading = false;
Swal.fire({
text: ' با موفقیت حذف شد.',
icon: 'success',
confirmButtonText: 'قبول'
}).then((result) => {
this.loadData();
});
})
this.items = this.items.filter(item => item.id !== id); // حذف از لیست محلی
this.$props.stat.count = this.items.length; // بهروزرسانی تعداد
this.snackbarText = 'یادداشت با موفقیت حذف شد.';
this.snackbarColor = 'success';
this.snackbar = true;
}).catch((error) => {
this.loading = false;
this.snackbarText = 'خطایی در حذف یادداشت رخ داد.';
this.snackbarColor = 'error';
this.snackbar = true;
console.error('خطا:', error);
});
},
save() {
if (this.des.trim() == '') {
Swal.fire({
text: 'شرح الزامی است.',
icon: 'error',
confirmButtonText: 'قبول'
})
}
else {
if (this.des.trim() === '') {
this.snackbarText = 'شرح الزامی است.';
this.snackbarColor = 'error';
this.snackbar = true;
} else {
this.loading = true;
axios.post('/api/notes/add', {
des: this.des,
type: this.$props.typeNote,
code: this.$route.params.id
}).then((response) => {
this.loading = false;
Swal.fire({
text: ' با موفقیت ثبت شد.',
icon: 'success',
confirmButtonText: 'قبول'
}).then((result) => {
this.loadData();
this.des = '';
axios
.post('/api/notes/add', {
des: this.des,
type: this.$props.typeNote,
code: this.$route.params.id,
})
.then((response) => {
this.loading = false;
// اضافه کردن یادداشت جدید به لیست محلی
this.items.unshift({
id: response.data.id, // فرض میکنیم سرور id رو برمیگردونه
des: this.des,
});
this.$props.stat.count = this.items.length; // بهروزرسانی تعداد
this.snackbarText = 'یادداشت با موفقیت ثبت شد.';
this.snackbarColor = 'success';
this.snackbar = true;
this.des = ''; // ریست کردن ورودی
})
.catch((error) => {
this.loading = false;
this.snackbarText = 'خطایی در ثبت یادداشت رخ داد.';
this.snackbarColor = 'error';
this.snackbar = true;
console.error('خطا:', error);
});
})
}
}
},
},
mounted() {
this.loadData();
}
})
});
</script>
<template>
<!-- Modal -->
<div class="modal modal-lg fade" id="notesModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1"
aria-labelledby="notesModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-warning text-white">
<h1 class="modal-title fs-5" id="notesModalLabel">
<i class="fa-regular fa-note-sticky me-1"></i>
یادداشتها
</h1>
<div class="block-options">
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
</div>
<div class="modal-body">
<div class="input-group mb-3">
<input v-model="des" type="text" class="form-control" placeholder="شرح" aria-label="افزودن یادداشت"
aria-describedby="button-addon1">
<button :disabled="this.loading" @click="save()" class="btn btn-outline-success" type="button"
id="button-addon1">ثبت</button>
</div>
<Loading color="blue" loader="dots" v-model:active="loading" :is-full-page="false"/>
<ul :disabled="this.loading" class="list-group">
<li v-for="item in items" class="list-group-item d-flex justify-content-between align-items-center">
<span class="">
{{ item.des }}
</span>
<a title="حذف" @click="remove(item.id)" class="text-danger rounded-pill float-start">
<i class="fa fa-trash"></i>
</a>
</li>
</ul>
<div v-if="items.length == 0">
نتیجهای یافت نشد
</div>
</div>
</div>
</div>
</div>
<!-- دکمه در تولبار -->
<v-btn icon color="warning" class="ml-2" @click="dialog = true">
<v-badge :content="items.length.toString()" color="dark">
<v-icon>mdi-note</v-icon>
</v-badge>
<v-tooltip activator="parent" location="bottom">یادداشتها</v-tooltip>
</v-btn>
<!-- دیالوگ یادداشتها -->
<v-dialog v-model="dialog" max-width="600" persistent>
<v-card>
<v-toolbar color="toolbar" flat dark>
<v-toolbar-title>
<v-icon color="warning" left>mdi-note</v-icon>
یادداشتها
</v-toolbar-title>
<v-spacer></v-spacer>
<v-btn icon @click="dialog = false">
<v-icon>mdi-close</v-icon>
</v-btn>
</v-toolbar>
<v-card-text>
<!-- فرم افزودن یادداشت -->
<v-row>
<v-col cols="12">
<v-text-field
v-model="des"
label="شرح"
placeholder="شرح یادداشت"
outlined
class="mt-2"
:disabled="loading"
@keyup.enter="save"
:loading="loading"
></v-text-field>
<v-btn
color="success"
@click="save"
class="mt-2"
:loading="loading"
>
<v-icon left>mdi-content-save</v-icon>
ثبت یادداشت
</v-btn>
</v-col>
</v-row>
<v-divider class="my-4"></v-divider>
<!-- لیست یادداشتها با اسکرول -->
<v-row>
<v-col cols="12">
<h5 class="text-primary">یادداشتهای ثبتشده</h5>
<v-list
max-height="300"
class="overflow-y-auto"
>
<v-list-item
v-for="item in items"
:key="item.id"
class="my-1"
>
<v-list-item-title class="d-flex align-center">
<span>{{ item.des }}</span>
<v-spacer></v-spacer>
<v-btn
icon
variant="plain"
:disabled="loading"
@click="remove(item.id)"
>
<v-icon color="error">mdi-trash-can</v-icon>
<v-tooltip activator="parent" location="top">حذف</v-tooltip>
</v-btn>
</v-list-item-title>
</v-list-item>
<v-list-item v-if="items.length === 0">
<v-list-item-title class="text-muted text-center">
یادداشتی موجود نیست
</v-list-item-title>
</v-list-item>
</v-list>
</v-col>
</v-row>
</v-card-text>
</v-card>
</v-dialog>
<!-- اسنکبار برای نمایش پیامها -->
<v-snackbar
v-model="snackbar"
:color="snackbarColor"
timeout="3000"
location="top"
>
{{ snackbarText }}
<template v-slot:actions>
<v-btn
color="white"
variant="text"
@click="snackbar = false"
>
بستن
</v-btn>
</template>
</v-snackbar>
</template>
<style scoped></style>
<style scoped>
.overflow-y-auto {
overflow-y: auto;
}
</style>

View file

@ -1,132 +1,120 @@
<script lang="ts">
import { defineComponent, ref } from 'vue'
import axios from "axios";
import Swal from "sweetalert2";
import rec from "../component/rec.vue";
import recList from "../component/recList.vue";
import ArchiveUpload from "../component/archive/archiveUpload.vue";
import type { Header, Item } from "vue3-easy-data-table";
import { getApiUrl } from "@/hesabixConfig";
import notes from '../component/notes.vue';
import { defineComponent, ref } from 'vue';
import axios from 'axios';
import Swal from 'sweetalert2';
import Rec from '../component/rec.vue';
import RecList from '../component/recList.vue';
import ArchiveUpload from '../component/archive/archiveUpload.vue';
import Notes from '../component/notes.vue';
import PrintOptions from '@/components/widgets/PrintOptions.vue';
import ShareOptions from '@/components/widgets/ShareOptions.vue';
import { getApiUrl } from '@/hesabixConfig';
export default defineComponent({
name: "viewInvoice",
name: 'viewInvoice',
components: {
ArchiveUpload,
rec: rec,
recList: recList,
notes
Rec,
RecList,
Notes,
PrintOptions,
ShareOptions,
},
watch: {
'PayWindowsState.submited'(newValue, oldValue) {
'PayWindowsState.submited'(newValue) {
this.PayWindowsState.submited = false;
if (newValue) {
this.loadData();
this.recModal.hide()
this.recDialog = false;
}
},
'recListWindowsState.submited'(newValue, oldValue) {
'recListWindowsState.submited'(newValue) {
this.recListWindowsState.submited = false;
if (newValue) {
this.loadData();
}
}
},
},
data: () => {
return {
printOptions: {
pays: true,
note: true,
bidInfo: true,
taxInfo: true,
discountInfo: true,
paper: 'A4-L'
},
notes: {
count: 0
},
PayWindowsState: {
submited: false
},
recListWindowsState: {
submited: false
},
recModal: {},
recListModal: {},
loading: ref(true),
shortlink_url: '',
copy_label: 'کپی',
send_message_label: 'ارسال',
bid: {
legal_name: '',
shortlinks: false,
},
item: {
doc: {
id: 0,
date: null,
code: null,
des: '',
amount: 0,
profit: 0,
shortLink: null,
},
relatedDocs: [],
rows: []
},
person: {
nikename: null,
mobile: '',
tel: '',
addres: '',
postalcode: '',
},
commoditys: [],
totalRec: 0,
totalDiscount: 0,
totalTax: 0,
transferCost: 0,
discountAll: 0,
mobileHeaders: [
{ text: "کالا", value: "commodity.name" },
{ text: "تعداد", value: "count" },
{ text: "مبلغ کل", value: "sumTotal" },
]
}
},
setup() {
data: () => ({
recDialog: false,
recListDialog: false,
activeTab: 'invoice-info',
loading: ref(true),
shortlink_url: '',
PayWindowsState: { submited: false },
recListWindowsState: { submited: false },
notes: { count: 0 },
bid: { legal_name: '', shortlinks: false },
item: {
doc: { id: 0, date: null, code: null, des: '', amount: 0, profit: 0, shortLink: null },
relatedDocs: [],
rows: [],
},
person: { nikename: null, mobile: '', tel: '', addres: '', postalcode: '' },
commoditys: [],
totalRec: 0,
totalDiscount: 0,
totalTax: 0,
transferCost: 0,
discountAll: 0,
mobileHeaders: [
{ title: 'کالا', key: 'commodity.name' },
{ title: 'تعداد', key: 'count' },
{ title: 'مبلغ کل', key: 'sumTotal' },
],
}),
computed: {
formattedAmount() {
return this.$filters.formatNumber(this.item.doc.amount);
},
statusText() {
return parseInt(this.item.doc.amount) <= parseInt(this.totalRec) ? 'تسویه شده' : 'تسویه نشده';
},
statusColor() {
return parseInt(this.item.doc.amount) <= parseInt(this.totalRec) ? 'success' : 'error';
},
profitText() {
return this.$filters.formatNumber(Math.abs(this.item.doc.profit));
},
profitLabel() {
return parseInt(this.item.doc.profit) >= 0 ? 'سود فاکتور' : 'زیان فاکتور';
},
profitColor() {
return parseInt(this.item.doc.profit) >= 0 ? 'success' : 'error';
},
formattedTotalTax() {
return this.$filters.formatNumber(this.totalTax);
},
formattedDiscountAll() {
return this.$filters.formatNumber(this.discountAll);
},
formattedTransferCost() {
return this.$filters.formatNumber(this.transferCost);
},
},
methods: {
copyToCliboard() {
navigator.clipboard.writeText(this.shortlink_url);
this.copy_label = 'کپی شد !';
},
loadData() {
this.loading = true;
this.commoditys = [];
axios.post('/api/accounting/doc/get', { 'code': this.$route.params.id }).then((response) => {
axios.post('/api/accounting/doc/get', { code: this.$route.params.id }).then((response) => {
this.item = response.data;
if (this.item.doc.shortLink != null) {
this.shortlink_url = getApiUrl() + '/sl/sell/' + localStorage.getItem("activeBid") + '/' + this.item.doc.shortLink;
}
else {
this.shortlink_url = getApiUrl() + '/sl/sell/' + localStorage.getItem("activeBid") + '/' + this.item.doc.id;
}
response.data.relatedDocs.forEach((rdoc: any) => {
this.totalRec += parseInt(rdoc.amount)
});
this.shortlink_url = this.item.doc.shortLink
? `${getApiUrl()}/sl/sell/${localStorage.getItem('activeBid')}/${this.item.doc.shortLink}`
: `${getApiUrl()}/sl/sell/${localStorage.getItem('activeBid')}/${this.item.doc.id}`;
this.totalRec = response.data.relatedDocs.reduce((sum: number, rdoc: any) => sum + parseInt(rdoc.amount), 0);
});
axios.get('/api/sell/get/info/' + this.$route.params.id).then((response) => {
axios.get(`/api/sell/get/info/${this.$route.params.id}`).then((response) => {
this.person = response.data.person;
this.discountAll = response.data.discountAll;
this.transferCost = response.data.transferCost;
this.item.doc.profit = response.data.profit;
response.data.rows.forEach((item: any) => {
if (item.commodity != null) {
this.commoditys = response.data.rows
.filter((item: any) => item.commodity != null)
.map((item: any) => {
this.totalTax += parseInt(item.tax);
this.totalDiscount += parseInt(item.discount);
this.commoditys.push({
return {
commodity: item.commodity,
count: item.commodity_count,
price: parseInt((parseInt(item.bs) - parseInt(item.tax) + parseInt(item.discount)) / parseFloat(item.commodity_count)),
@ -138,499 +126,197 @@ export default defineComponent({
tax: item.tax,
sumWithoutTax: item.bs - item.tax,
sumTotal: item.bs,
table: 53
});
}
});
table: 53,
};
});
});
axios.post('/api/business/get/info/' + localStorage.getItem('activeBid')).then((response) => {
axios.post(`/api/business/get/info/${localStorage.getItem('activeBid')}`).then((response) => {
this.bid = response.data;
this.loading = false;
});
axios.get("/api/printers/options/info").then((response) => {
this.loading = false;
this.printOptions = response.data.sell;
})
},
sendSMS() {
this.loading = true;
const regex = new RegExp("^(\\+98|0)?9\\d{9}$");
if (!regex.test(this.person.mobile)) {
Swal.fire({
text: 'شماره موبایل وارد شده نا معتبر است.',
icon: 'error',
confirmButtonText: 'قبول'
});
this.loading = false;
}
else {
this.send_message_label = 'در حال ارسال...';
axios.post('/api/sms/send/sell-invoice/' + this.item.doc.id + '/' + this.person.mobile).then((response) => {
if (response.data.result == 2) {
Swal.fire({
text: 'اعتبار سرویس پیامک کافی نیست.',
icon: 'error',
confirmButtonText: 'قبول'
});
this.send_message_label = 'ارسال';
}
else if (response.data.result == 1) {
Swal.fire({
text: 'پیامک اطلاع رسانی ارسال شد.',
icon: 'success',
confirmButtonText: 'قبول'
});
this.send_message_label = 'ارسال شد!'
}
this.loading = false;
})
}
},
printInvoice(pdf = true, cloudePrinters = true) {
axios.post('/api/sell/print/invoice', {
'code': this.$route.params.id,
'pdf': pdf,
'printers': cloudePrinters,
'printOptions': this.printOptions
}).then((response) => {
window.open(this.$API_URL + '/front/print/' + response.data.id, '_blank', 'noreferrer');
});
}
},
mounted() {
this.loadData();
this.recModal = new bootstrap.Modal(document.getElementById('rec-modal'))
this.recListModal = new bootstrap.Modal(document.getElementById('rec-list-modal'))
}
})
},
});
</script>
<template>
<!-- Print Modal -->
<div class="modal fade" id="printModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1"
aria-labelledby="printModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-primary-light text-white">
<h1 class="modal-title fs-5" id="printModalLabel">چاپ فاکتور</h1>
<div class="block-options">
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
</div>
<div class="modal-body">
<p class="mb-2">برای تغییر تنظیمات پیشفرض به بخش تنظیمات چاپ مراجعه نمایید</p>
<div class="form-floating mb-2">
<select v-model="printOptions.paper" class="form-select">
<option value="A4-L">A4 افقی</option>
<option value="A4">A4 عمودی</option>
<option value="A5-L">A5 افقی</option>
<option value="A5">A5 عمودی</option>
</select>
<label>سایز کاغذ و حالت چاپ</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" v-model="printOptions.bidInfo" type="checkbox">
<label class="form-check-label">اطلاعات کسبوکار</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" v-model="printOptions.pays" type="checkbox">
<label class="form-check-label">نمایش پرداختهای فاکتور</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" v-model="printOptions.note" type="checkbox">
<label class="form-check-label">یادداشت پایین فاکتور</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" v-model="printOptions.taxInfo" type="checkbox">
<label class="form-check-label">مالیات به تفکیک اقلام</label>
</div>
<div class="form-check form-switch">
<input class="form-check-input" v-model="printOptions.discountInfo" type="checkbox">
<label class="form-check-label">تخفیف به تفکیک اقلام</label>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary mx-2" @click="printInvoice()" type="button">
<i class="si si-printer me-1"></i>
<span class="">چاپ</span>
</button>
</div>
</div>
</div>
</div>
<!-- End Print Modal -->
<v-toolbar color="toolbar" flat title="مشاهده فاکتور">
<template v-slot:prepend>
<v-tooltip text="بازگشت" location="bottom">
<template v-slot:activator="{ props }">
<v-btn v-bind="props" @click="$router.back()" class="d-none d-sm-flex" variant="text" icon="mdi-arrow-right" />
</template>
</v-tooltip>
</template>
<v-spacer></v-spacer>
<v-btn icon v-if="item.doc.id !== 0">
<archive-upload :docid="item.doc.id" doctype="sell" cat="sell" />
<v-tooltip activator="parent" location="bottom">آرشیو</v-tooltip>
</v-btn>
<notes :stat="notes" :code="$route.params.id" type-note="sell" />
<v-btn icon color="error" class="ml-2" v-if="parseInt(item.doc.amount) > parseInt(totalRec)" @click="recDialog = true">
<v-icon>mdi-money</v-icon>
<v-tooltip activator="parent" location="bottom">ثبت دریافت</v-tooltip>
</v-btn>
<v-btn icon color="info" class="ml-2" @click="recListDialog = true">
<v-icon>mdi-arrow-down-circle</v-icon>
<v-tooltip activator="parent" location="bottom">دریافتها</v-tooltip>
</v-btn>
<share-options v-if="bid.shortlinks" :shortlink-url="shortlink_url" :mobile="person.mobile" :invoice-id="item.doc.id" />
<print-options :invoice-id="$route.params.id" />
</v-toolbar>
<div class="block block-content-full">
<div id="fixed-header" class="block-header block-header-default bg-gray-light">
<h3 class="block-title text-primary-dark">
<button @click="$router.back()" type="button"
class="float-start d-none d-sm-none d-md-block btn btn-sm btn-link text-warning">
<i class="fa fw-bold fa-arrow-right"></i>
</button>
<i class="fas fa-file-invoice-dollar"></i>
مشاهده فاکتور
</h3>
<div class="block-options">
<archive-upload v-if="item.doc.id != 0" :docid="item.doc.id" doctype="sell" cat="sell"></archive-upload>
<button type="button" class="btn btn-sm btn-warning text-light me-2" data-bs-toggle="modal"
data-bs-target="#notesModal">
<span class="badge text-bg-dark me-2">{{ notes.count }}</span>
<i class="fa-regular fa-note-sticky me-1"></i>
<span class="d-none d-sm-inline-block">یادداشتها</span>
</button>
<notes :stat="notes" :code="$route.params.id" typeNote="sell" />
<!-- Button trigger modal -->
<button v-show="parseInt(item.doc.amount) > parseInt(totalRec)" type="button" class="btn btn-sm btn-danger"
@click="recModal.show()">
<i class="fas fa-money-bill-1-wave"></i>
<span class="d-none d-sm-inline-block">ثبت دریافت</span>
</button>
<!-- Modal -->
<div class="modal fade" data-bs-backdrop="static" data-bs-keyboard="false" id="rec-modal" tabindex="-1"
aria-labelledby="exampleModalLabel1" aria-hidden="true">
<rec ref="submitPay" :windowsState="PayWindowsState" :person="person.id" :original-doc="item.doc.code"
:total-amount="parseInt(item.doc.amount) - parseInt(totalRec)">
</rec>
</div>
<button type="button" class="btn btn-sm btn-info ms-2" @click="recListModal.show()">
<i class="fas fa-arrow-alt-circle-down"></i>
<span class="d-none d-sm-inline-block">دریافتها</span>
</button>
<!-- Modal -->
<div class="modal fade" data-bs-backdrop="static" data-bs-keyboard="false" id="rec-list-modal" tabindex="-1"
aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabel1">
<i class="fas fa-arrow-alt-circle-down ms-2"></i>
دریافتها
</h1>
<div class="block-options">
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
</div>
<div class="modal-body">
<rec-list ref="recListRef" :windowsState="recListWindowsState" :items="item.relatedDocs"></rec-list>
</div>
<div class="modal-footer">
<button type="button" ref="btnCloseModalRec" class="btn btn-secondary btn-close-modal"
data-bs-dismiss="modal">بازگشت</button>
</div>
</div>
</div>
</div>
<!-- Button trigger modal -->
<button v-show="bid.shortlinks" type="button" class="btn btn-sm btn-success ms-2" data-bs-toggle="modal"
data-bs-target="#exampleModal">
<i class="fas fa-share-nodes"></i>
<span class="d-none d-sm-inline-block">اشتراک گذاری</span>
</button>
<!-- Modal -->
<div class="modal fade" data-bs-backdrop="static" data-bs-keyboard="false" id="exampleModal" tabindex="-1"
aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabel">
<i class="fas fa-share-nodes"></i>
اشتراک گذاری
</h1>
<div class="block-options">
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
</div>
<div class="modal-body">
<div class="container">
<div class="row">
<div class="input-group mb-2">
<div class="input-group-text">
<i class="fa fa-paperclip me-2"></i>
پیوند فاکتور
</div>
<input :readonly="true" type="text" class="form-control" v-model="shortlink_url">
<button class="btn btn-outline-success" type="button" @click="copyToCliboard()">{{ copy_label
}}</button>
</div>
<div class="input-group">
<div class="input-group-text">
<i class="fa fa-message me-2"></i>
ارسال پیامک
</div>
<input type="text" class="form-control" v-model="person.mobile">
<button :disabled="loading" class="btn btn-outline-success" type="button" @click="sendSMS()">{{
send_message_label }}</button>
</div>
</div>
<div class="mt-3">
<i class="fas fa-share-nodes me-3"></i>
<label>اشتراک گذاری در شبکههای اجتماعی</label>
</div>
<div class="mt-2">
<a target="_blank" :href="'tg://msg?text=' + shortlink_url">
<img src="/img/icons/telegram.png" class="m-3" style="max-width: 30px;" />
</a>
<a target="_blank" :href="'et://msg_url?url=' + shortlink_url">
<img src="/img/icons/eitaa.jpeg" class="m-3" style="max-width: 30px;" />
</a>
<a target="_blank" :href="'https://ble.ir/share/url?url=' + shortlink_url">
<img src="/img/icons/bale-logo.png" class="m-3" style="max-width: 30px;" />
</a>
<a target="_blank" :href="'https://ble.ir/share/url?url=' + shortlink_url">
<img src="/img/icons/robika.png" class="m-3" style="max-width: 30px;" />
</a>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">بازگشت</button>
</div>
</div>
</div>
</div>
<!-- print trigger modal -->
<button type="button" class="btn btn-sm btn-primary mx-2" data-bs-toggle="modal" data-bs-target="#printModal">
<i class="si si-printer me-1"></i>
<span class="d-none d-sm-inline-block">چاپ فاکتور</span>
</button>
</div>
</div>
<div class="block-content py-2 mt-2">
<div class="row">
<div class="col-12">
<div class="row">
<div class="col-sm-6 col-md-4">
<div class="input-group input-group-sm mb-3">
<span class="input-group-text" id="inputGroup-sizing-sm">شماره</span>
<input type="text" :readonly="true" v-model="item.doc.code" class="form-control"
aria-describedby="inputGroup-sizing-sm">
</div>
</div>
<div class="col-sm-6 col-md-4">
<div class="input-group input-group-sm mb-3">
<span class="input-group-text" id="inputGroup-sizing-sm">تاریخ</span>
<input type="text" :readonly="true" v-model="item.doc.date" class="form-control"
aria-describedby="inputGroup-sizing-sm">
</div>
</div>
<div class="col-sm-12 col-md-4">
<div class="input-group input-group-sm mb-3">
<span class="input-group-text" id="inputGroup-sizing-sm">شرح</span>
<input type="text" :readonly="true" v-model="item.doc.des" class="form-control"
aria-describedby="inputGroup-sizing-sm">
</div>
</div>
</div>
<b class="ps-2">خریدار</b>
<div class="row">
<div class="col-sm-6 col-md-4">
<div class="input-group input-group-sm mb-3">
<span class="input-group-text" id="inputGroup-sizing-sm">نام</span>
<input type="text" :readonly="true" v-model="person.nikename" class="form-control"
aria-describedby="inputGroup-sizing-sm">
</div>
</div>
<div class="col-sm-6 col-md-4">
<div class="input-group input-group-sm mb-3">
<span class="input-group-text" id="inputGroup-sizing-sm">موبایل</span>
<input type="text" :readonly="true" v-model="person.mobile" class="form-control"
aria-describedby="inputGroup-sizing-sm">
</div>
</div>
<div class="col-sm-6 col-md-4">
<div class="input-group input-group-sm mb-3">
<span class="input-group-text" id="inputGroup-sizing-sm">تلفن</span>
<input type="text" :readonly="true" v-model="person.tel" class="form-control"
aria-describedby="inputGroup-sizing-sm">
</div>
</div>
<div class="col-sm-6 col-md-3">
<div class="input-group input-group-sm mb-3">
<span class="input-group-text" id="inputGroup-sizing-sm">کد پستی</span>
<input type="text" :readonly="true" v-model="person.postalcode" class="form-control"
aria-describedby="inputGroup-sizing-sm">
</div>
</div>
<div class="col-sm-12 col-md-9">
<div class="input-group input-group-sm mb-3">
<span class="input-group-text" id="inputGroup-sizing-sm">آدرس</span>
<input type="text" :readonly="true" v-model="person.address" class="form-control"
aria-describedby="inputGroup-sizing-sm">
</div>
</div>
</div>
<b class="ps-2">اقلام</b>
<EasyDataTable table-class-name="customize-table" :headers="mobileHeaders" :items="commoditys" show-index
alternating theme-color="#1d90ff" header-text-direction="center" body-text-direction="center"
rowsPerPageMessage="تعداد سطر" emptyMessage="اطلاعاتی برای نمایش وجود ندارد" rowsOfPageSeparatorMessage="از"
:loading="loading">
<template #item-sumTotal="{ sumTotal }">
{{ $filters.formatNumber(sumTotal) }}
</template>
<template #item-count="{ count, commodity }">
{{ count }} {{ commodity.unit }}
</template>
<template #expand="{ tax, discount, des, price }">
<div class="p-1 m-0">
<ul class="list-group">
<li class="list-group-item d-flex justify-content-between align-items-center">
قیمت واحد
<span class="badge text-bg-primary rounded-pill">
{{ $filters.formatNumber(price) }}
</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
تخفیف
<span class="badge text-bg-primary rounded-pill">
{{ $filters.formatNumber(discount) }}
</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
مالیات
<span class="badge text-bg-primary rounded-pill">
{{ $filters.formatNumber(tax) }}
</span>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
شرح:
{{ des }}
</li>
</ul>
</div>
</template>
</EasyDataTable>
<div class="row pt-2">
<div class="col-sm-6 col-md-4">
<div class="input-group input-group-sm mb-3">
<span class="input-group-text" id="inputGroup-sizing-sm">جمع کل</span>
<input type="text" :readonly="true" :value="$filters.formatNumber(item.doc.amount)"
class="form-control" aria-describedby="inputGroup-sizing-sm">
</div>
</div>
<div class="col-sm-6 col-md-4">
<div class="input-group input-group-sm mb-3">
<span class="input-group-text" id="inputGroup-sizing-sm">وضعیت</span>
<input v-if="parseInt(item.doc.amount) <= parseInt(totalRec)" type="text" :readonly="true"
value="تسویه شده" class="form-control text-success" aria-describedby="inputGroup-sizing-sm">
<input v-else type="text" :readonly="true" value="تسویه نشده" class="form-control text-danger"
aria-describedby="inputGroup-sizing-sm">
</div>
</div>
<div class="col-sm-6 col-md-4">
<div v-if="parseInt(item.doc.profit) >= 0" class="input-group input-group-sm mb-3">
<span class="input-group-text" id="inputGroup-sizing-sm">سود فاکتور</span>
<input type="text" :readonly="true" :value="$filters.formatNumber(Math.abs(item.doc.profit))"
class="form-control text-success">
</div>
<div v-else class="input-group input-group-sm mb-3">
<span class="input-group-text" id="inputGroup-sizing-sm">زیان فاکتور</span>
<input type="text" :readonly="true" :value="$filters.formatNumber(Math.abs(item.doc.profit))"
class="form-control text-danger">
</div>
</div>
</div>
<div class="row">
<div class="col-sm-6 col-md-4">
<div class="input-group input-group-sm mb-3">
<span class="input-group-text" id="inputGroup-sizing-sm">مالیات</span>
<input type="text" :readonly="true" :value="$filters.formatNumber(totalTax)" class="form-control"
aria-describedby="inputGroup-sizing-sm">
</div>
</div>
<div class="col-sm-6 col-md-4">
<div class="input-group input-group-sm mb-3">
<span class="input-group-text" id="inputGroup-sizing-sm">تخفیف</span>
<input type="text" :readonly="true" :value="$filters.formatNumber(discountAll)" class="form-control"
aria-describedby="inputGroup-sizing-sm">
</div>
</div>
<div class="col-sm-6 col-md-4">
<div class="input-group input-group-sm mb-3">
<span class="input-group-text" id="inputGroup-sizing-sm">هزینه حمل و نقل</span>
<input type="text" :readonly="true" :value="$filters.formatNumber(transferCost)" class="form-control"
aria-describedby="inputGroup-sizing-sm">
</div>
</div>
</div>
<div class="block block-rounded">
<div class="block-header block-header-default">
<h3 class="block-title">دریافتها</h3>
<div class="block-options">
</div>
</div>
<div class="block-content p-0">
<table v-if="item.relatedDocs.length != 0"
class="table border-0 table-borderless table-striped table-vcenter fs-sm">
<thead class="bg-primary text-light">
<tr class="text-center">
<th>مشاهده</th>
<th>شماره</th>
<th>تاریخ</th>
<th>مبلغ</th>
</tr>
</thead>
<tbody>
<tr v-for="rd in item.relatedDocs" class="text-center">
<td>
<router-link :to="'/acc/accounting/view/' + rd.code">
<span class="text-success fa fa-eye"></span>
</router-link>
</td>
<td class="fw-semibold" style="width: 100px;">
{{ rd.code }}
</td>
<td class="fw-semibold">
{{ rd.date }}
</td>
<td class="fw-semibold">
{{ $filters.formatNumber(rd.amount) }}
</td>
</tr>
<tr v-if="item.relatedDocs.length == 0" class="text-center">
<td colspan="4">
سند پرداختی تاکنون ثبت نشده است.
</td>
</tr>
</tbody>
</table>
<div class="text-center py-2 text-danger" v-else>
<span>تاکنون سند دریافتی ثبت نشده است</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<v-tabs v-model="activeTab" color="primary" grow class="mt-0">
<v-tab value="person-info">اطلاعات شخص</v-tab>
<v-tab value="invoice-info">اقلام فاکتور</v-tab>
<v-tab value="payments">دریافتها</v-tab>
</v-tabs>
<v-container fluid>
<v-window v-model="activeTab">
<!-- تب اطلاعات شخص -->
<v-window-item value="person-info">
<v-card-text>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="person.nikename" label="نام" outlined readonly></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="person.mobile" label="موبایل" outlined readonly></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="person.tel" label="تلفن" outlined readonly></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="3">
<v-text-field v-model="person.postalcode" label="کد پستی" outlined readonly></v-text-field>
</v-col>
<v-col cols="12" sm="12" md="9">
<v-text-field v-model="person.addres" label="آدرس" outlined readonly></v-text-field>
</v-col>
</v-row>
</v-card-text>
</v-window-item>
<!-- تب اطلاعات فاکتور -->
<v-window-item value="invoice-info">
<v-card-text>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="item.doc.code" label="شماره" outlined readonly></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="item.doc.date" label="تاریخ" outlined readonly></v-text-field>
</v-col>
<v-col cols="12" sm="12" md="4">
<v-text-field v-model="item.doc.des" label="شرح" outlined readonly></v-text-field>
</v-col>
</v-row>
<v-list-subheader>اقلام</v-list-subheader>
<v-data-table :headers="mobileHeaders" :items="commoditys" :loading="loading" show-expand class="elevation-1">
<template v-slot:item.sumTotal="{ item }">
{{ $filters.formatNumber(item.sumTotal) }}
</template>
<template v-slot:item.count="{ item }">
{{ item.count }} {{ item.commodity.unit }}
</template>
<template v-slot:expanded-row="{ item }">
<v-list dense>
<v-list-item>
<v-list-item-title>قیمت واحد</v-list-item-title>
<v-list-item-subtitle>{{ $filters.formatNumber(item.price) }}</v-list-item-subtitle>
</v-list-item>
<v-list-item>
<v-list-item-title>تخفیف</v-list-item-title>
<v-list-item-subtitle>{{ $filters.formatNumber(item.discount) }}</v-list-item-subtitle>
</v-list-item>
<v-list-item>
<v-list-item-title>مالیات</v-list-item-title>
<v-list-item-subtitle>{{ $filters.formatNumber(item.tax) }}</v-list-item-subtitle>
</v-list-item>
<v-list-item>
<v-list-item-title>شرح</v-list-item-title>
<v-list-item-subtitle>{{ item.des }}</v-list-item-subtitle>
</v-list-item>
</v-list>
</template>
</v-data-table>
<v-row class="mt-2">
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="formattedAmount" label="جمع کل" outlined readonly></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="statusText" :color="statusColor" label="وضعیت" outlined readonly></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="profitText" :label="profitLabel" :color="profitColor" outlined readonly></v-text-field>
</v-col>
</v-row>
<v-row>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="formattedTotalTax" label="مالیات" outlined readonly></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="formattedDiscountAll" label="تخفیف" outlined readonly></v-text-field>
</v-col>
<v-col cols="12" sm="6" md="4">
<v-text-field v-model="formattedTransferCost" label="هزینه حمل و نقل" outlined readonly></v-text-field>
</v-col>
</v-row>
</v-card-text>
</v-window-item>
<!-- تب دریافتها -->
<v-window-item value="payments">
<v-card-text>
<v-data-table v-if="item.relatedDocs.length" :headers="[
{ title: 'مشاهده', key: 'view' },
{ title: 'شماره', key: 'code' },
{ title: 'تاریخ', key: 'date' },
{ title: 'مبلغ', key: 'amount' },
]" :items="item.relatedDocs">
<template v-slot:item.view="{ item }">
<router-link :to="'/acc/accounting/view/' + item.code">
<v-icon color="success">mdi-eye</v-icon>
</router-link>
</template>
<template v-slot:item.amount="{ item }">
{{ $filters.formatNumber(item.amount) }}
</template>
</v-data-table>
<v-card-text v-else class="text-center text-danger">
تاکنون سند دریافتی ثبت نشده است
</v-card-text>
</v-card-text>
</v-window-item>
</v-window>
<v-dialog v-model="recDialog" max-width="600">
<rec :windows-state="PayWindowsState" :person="person.id" :original-doc="item.doc.code" :total-amount="parseInt(item.doc.amount) - parseInt(totalRec)" />
</v-dialog>
<v-dialog v-model="recListDialog" max-width="800">
<v-card>
<v-card-title>
<v-icon left>mdi-arrow-down-circle</v-icon>
دریافتها
</v-card-title>
<v-card-text>
<rec-list :windows-state="recListWindowsState" :items="item.relatedDocs" />
</v-card-text>
<v-card-actions>
<v-btn color="secondary" @click="recListDialog = false">بازگشت</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</v-container>
</template>
<style scoped>
table {
font-size: small;
border: 1px solid gray;
}
.table-header {
background-color: lightgray;
}
.c-print {
background-color: white;
}
@media print {
@page {
margin-top: 0;
margin-bottom: 0;
}
body {
padding-top: 72px;
padding-bottom: 72px;
}
}
/* استایل‌ها */
</style>