Skip to content

Commit

Permalink
Merge pull request #4216 from mikhailprivalov/cheque
Browse files Browse the repository at this point in the history
Cheque
  • Loading branch information
urchinpro authored Aug 26, 2024
2 parents 0b14428 + caae6e7 commit 4dd3bb0
Show file tree
Hide file tree
Showing 8 changed files with 371 additions and 3 deletions.
1 change: 1 addition & 0 deletions api/cash_registers/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
path('open-shift', views.open_shift),
path('close-shift', views.close_shift),
path('get-shift-data', views.get_shift_data),
path('get-services-coasts', views.get_services_coasts),
]
7 changes: 7 additions & 0 deletions api/cash_registers/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,10 @@ def close_shift(request):
def get_shift_data(request):
result = cash_register_views.get_shift_data(request.user.doctorprofile.id)
return JsonResponse(result)


@login_required
def get_services_coasts(request):
request_data = json.loads(request.body)
result = cash_register_views.get_service_coasts(request_data["serviceIds"])
return JsonResponse(result)
31 changes: 31 additions & 0 deletions cash_registers/sql_func.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,34 @@ def check_shift(cash_register_id, doctor_profile_id):
)
rows = namedtuplefetchall(cursor)
return rows


def get_service_coasts(services_ids):
with connection.cursor() as cursor:
cursor.execute(
"""
SELECT directory_researches.id as research_id, directory_researches.title, contracts_pricecoast.coast FROM directions_istochnikifinansirovaniya
INNER JOIN clients_cardbase ON directions_istochnikifinansirovaniya.base_id = clients_cardbase.id
INNER JOIN contracts_contract ON directions_istochnikifinansirovaniya.contracts_id = contracts_contract.id
INNER JOIN contracts_pricecoast ON contracts_contract.price_id = contracts_pricecoast.price_name_id
INNER JOIN directory_researches ON contracts_pricecoast.research_id = directory_researches.id
WHERE LOWER(directions_istochnikifinansirovaniya.title) = 'платно' and clients_cardbase.internal_type = true
AND contracts_pricecoast.research_id in %(services_ids)s
""",
params={"services_ids": services_ids},
)
rows = namedtuplefetchall(cursor)
return rows


def get_services(services_ids):
with connection.cursor() as cursor:
cursor.execute(
"""
SELECT directory_researches.id, directory_researches.title FROM directory_researches
WHERE directory_researches.id in %(services_ids)s
""",
params={"services_ids": services_ids},
)
rows = namedtuplefetchall(cursor)
return rows
27 changes: 26 additions & 1 deletion cash_registers/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from cash_registers.models import CashRegister, Shift
import cash_registers.req as cash_req
import cash_registers.sql_func as sql_func
from laboratory.settings import TIME_ZONE


Expand Down Expand Up @@ -51,7 +52,9 @@ def get_shift_data(doctor_profile_id: int):
shift_status = shift.get_shift_status()
current_status = shift_status["status"]
uuid_data = shift_status["uuid"]
open_at = shift.open_at.astimezone(pytz.timezone(TIME_ZONE)).strftime('%d.%m.%Y %H:%M')
open_at = ""
if shift.open_at:
open_at = shift.open_at.astimezone(pytz.timezone(TIME_ZONE)).strftime('%d.%m.%Y %H:%M')
result["data"] = {"shiftId": shift.pk, "cashRegisterId": shift.cash_register_id, "cashRegisterTitle": shift.cash_register.title, "open_at": open_at, "status": current_status}

if uuid_data:
Expand All @@ -72,3 +75,25 @@ def get_shift_data(doctor_profile_id: int):
else:
result = check_cash_register
return result


def get_service_coasts(service_ids: list):
if not service_ids:
return
service_ids_tuple = tuple(service_ids)
service_without_coast = False
summ = 0
services = sql_func.get_services(service_ids_tuple)
services_coast = {service.id: {"id": service.id, "title": service.title, "coast": 0} for service in services}
coasts = sql_func.get_service_coasts(service_ids_tuple)

for coast in coasts:
services_coast[coast.research_id]["coast"] = coast.coast
summ += coast.coast

if len(coasts) < len(service_ids):
service_without_coast = True

result = {"coasts": [i for i in services_coast.values()], "summ": summ, "serviceWithoutCoast": service_without_coast}

return result
2 changes: 1 addition & 1 deletion l2-frontend/src/components/Navbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ import Component from 'vue-class-component';
import { mapGetters } from 'vuex';
import NavbarDropdownContent from '@/components/NavbarDropdownContent.vue';
import shiftModal from '@/ui-cards/ShiftModal.vue';
import shiftModal from '@/ui-cards/CashRegisters/ShiftModal.vue';
@Component({
computed: mapGetters([
Expand Down
278 changes: 278 additions & 0 deletions l2-frontend/src/ui-cards/CashRegisters/ChequeModal.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,278 @@
<template>
<transition name="fade">
<Modal
show-footer="true"
ignore-body
white-bg="true"
max-width="710px"
width="100%"
margin-left-right="auto"
@close="closeModal"
>
<span
v-if="!loading"
slot="header"
>{{ 'Чек' }}</span>
<span
v-if="loading"
slot="header"
class="text-center"
>{{ 'Загрузка...' }}</span>
<div
slot="body"
>
<div
v-if="shiftIsOpen"
class="body"
>
<div
class="scroll"
>
<table class="table">
<colgroup>
<col>
<col style="width: 100px">
</colgroup>
<thead class="sticky">
<tr>
<th class="text-center">
<strong>Услуга</strong>
</th>
<th class="text-center">
<strong>Цена</strong>
</th>
</tr>
</thead>
<tr
v-for="service in servicesCoasts"
:key="service.id"
>
<VueTippyTd
class="text-left padding service-title border"
:text="service.title"
/>
<td class="text-center border padding">
{{ service.coast }}
</td>
</tr>
<tfoot class="sticky-footer">
<tr>
<td class="text-right">
<strong>
Итого:
</strong>
</td>
<td class="text-center">
<strong>
{{ summServiceCoasts }}
</strong>
</td>
</tr>
</tfoot>
</table>
</div>
<div class="flex-space">
<div class="flex">
<div class="input-width">
<label>Наличными</label>
<input
v-model.number="paymentCash"
type="number"
class="form-control"
step="0.01"
min="0"
:max="maxPay.cash"
>
</div>
<div class="input-width">
<label>Картой</label>
<input
v-model.number="paymentCard"
type="number"
class="form-control"
step="0.01"
min="0"
:max="maxPay.card"
>
</div>
</div>
<div class="discount-width">
<label>Скидка (%)</label>
<input
v-model.number="discount"
type="number"
class="form-control"
min="0"
step="1"
max="100"
>
</div>
</div>
<div>
<h5>К оплате {{ summForPay.toFixed(2) }}</h5>
</div>
<div
v-if="paymentCash"
class="input-width"
>
<label>Получено наличными</label>
<input
v-model.number="receivedCash"
type="number"
class="form-control"
>
<h5>Сдача: {{ cashChange.toFixed(2) }}</h5>
</div>
</div>
<h4 v-else>
Смена не открыта
</h4>
</div>
<div slot="footer">
<div class="row">
<div class="col-xs-4">
<button
class="btn btn-primary-nb btn-blue-nb"
type="button"
@click="closeModal"
>
Закрыть
</button>
</div>
</div>
</div>
</Modal>
</transition>
</template>

<script setup lang="ts">
import {
computed,
onMounted, ref,
} from 'vue';
import Modal from '@/ui-cards/Modal.vue';
import { useStore } from '@/store';
import * as actions from '@/store/action-types';
import api from '@/api';
import VueTippyTd from '@/construct/VueTippyTd.vue';
const store = useStore();
const emit = defineEmits(['closeModal']);
const props = defineProps({
serviceIds: {
type: Array,
required: true,
},
});
const cashRegister = computed(() => store.getters.cashRegisterShift);
const shiftIsOpen = computed(() => !!cashRegister.value?.cashRegisterId);
const loading = ref(false);
const closeModal = () => {
emit('closeModal');
};
const servicesCoasts = ref([]);
const summServiceCoasts = ref(0);
const noCoast = ref(false);
const getServicesCoasts = async () => {
await store.dispatch(actions.INC_LOADING);
const { coasts, summ, serviceWithoutCoast } = await api('cash-register/get-services-coasts', { serviceIds: props.serviceIds });
await store.dispatch(actions.DEC_LOADING);
servicesCoasts.value = coasts;
summServiceCoasts.value = Number(summ);
noCoast.value = serviceWithoutCoast;
};
onMounted(async () => {
await getServicesCoasts();
});
const paymentCash = ref(0);
const paymentCard = ref(0);
const discount = ref(0);
const summForPay = computed(() => {
if (discount.value >= 0 && discount.value <= 100) {
const summDiscount = (summServiceCoasts.value * discount.value) / 100;
return summServiceCoasts.value - summDiscount;
}
if (discount.value < 0) {
return summServiceCoasts.value;
}
return 0.00;
});
const receivedCash = ref(0);
const cashChange = computed(() => {
if (receivedCash.value && receivedCash.value >= paymentCash.value) {
return receivedCash.value - paymentCash.value;
}
return 0;
});
const maxPay = computed(() => {
const summForPayNumber = Number(summForPay.value.toFixed(2));
const card = summForPayNumber - paymentCash.value;
const cash = summForPayNumber - paymentCard.value;
return { card: Number(card.toFixed(2)), cash: Number(cash.toFixed(2)) };
});
</script>

<style scoped lang="scss">
.body {
height: 500px;
}
.flex {
display: flex;
}
.scroll {
min-height: 106px;
height: calc(100% - 200px);
overflow-y: auto;
}
.table {
margin-bottom: 0;
table-layout: fixed;
}
.sticky {
position: sticky;
top: 0;
z-index: 1;
background-color: white;
}
.sticky-footer {
position: sticky;
bottom: 0;
z-index: 1;
background-color: white;
}
.table > thead > tr > th {
border-bottom: 0;
}
.padding {
padding: 2px 0 2px 6px
}
.service-title {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.border {
border: 1px solid #ddd;
}
.flex-space {
display: flex;
justify-content: space-between;
}
.input-width {
width: 165px;
}
.discount-width {
width: 90px;
}
</style>
Loading

0 comments on commit 4dd3bb0

Please sign in to comment.