-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Loading status checks…
Merge pull request #4163 from mikhailprivalov/cash-register-
Cash register
- Loading branch information
Showing
22 changed files
with
653 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from django.urls import path | ||
from . import views | ||
|
||
urlpatterns = [ | ||
path('get-cash-registers', views.get_cash_registers), | ||
path('open-shift', views.open_shift), | ||
path('close-shift', views.close_shift), | ||
path('get-shift-data', views.get_shift_data), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import json | ||
from django.contrib.auth.decorators import login_required | ||
from django.http import JsonResponse | ||
import cash_registers.views as cash_register_views | ||
from cash_registers.models import Shift | ||
|
||
|
||
@login_required | ||
def get_cash_registers(request): | ||
result = cash_register_views.get_cash_registers() | ||
return JsonResponse({"result": result}) | ||
|
||
|
||
@login_required | ||
def open_shift(request): | ||
request_data = json.loads(request.body) | ||
check_shift = Shift.check_shift(request_data["cashRegisterId"], request.user.doctorprofile.id) | ||
if check_shift["ok"]: | ||
result = cash_register_views.open_shift(request_data["cashRegisterId"], request.user.doctorprofile.id) | ||
else: | ||
result = check_shift | ||
return JsonResponse(result) | ||
|
||
|
||
@login_required | ||
def close_shift(request): | ||
request_data = json.loads(request.body) | ||
result = cash_register_views.close_shift(request_data["cashRegisterId"], request.user.doctorprofile.id) | ||
return JsonResponse(result) | ||
|
||
|
||
@login_required | ||
def get_shift_data(request): | ||
result = cash_register_views.get_shift_data(request.user.doctorprofile.id) | ||
return JsonResponse(result) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
from django.contrib import admin | ||
from cash_registers.models import CashRegister, Shift | ||
|
||
|
||
class CashRegisterAdmin(admin.ModelAdmin): | ||
list_display = ['pk', 'title', 'ip_address', 'port', 'department', 'address'] | ||
search_fields = ( | ||
'pk', | ||
'title', | ||
) | ||
raw_id_fields = ['department'] | ||
|
||
|
||
class ShiftAdmin(admin.ModelAdmin): | ||
list_display = ['pk', 'cash_register', 'open_at', 'close_at', 'operator'] | ||
search_fields = ('pk', 'operator', 'cash_register') | ||
list_filter = ('operator',) | ||
raw_id_fields = ['operator', 'cash_register'] | ||
|
||
|
||
admin.site.register(CashRegister, CashRegisterAdmin) | ||
admin.site.register(Shift, ShiftAdmin) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from django.apps import AppConfig | ||
|
||
|
||
class CashRegistersConfig(AppConfig): | ||
default_auto_field = 'django.db.models.BigAutoField' | ||
name = 'cash_registers' |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
import uuid | ||
from django.db import models | ||
from jsonfield import JSONField | ||
from cash_registers import sql_func | ||
from laboratory.utils import current_time | ||
from podrazdeleniya.models import Podrazdeleniya | ||
from users.models import DoctorProfile | ||
|
||
|
||
class CashRegister(models.Model): | ||
title = models.CharField(max_length=255, blank=True, null=True, verbose_name='Наименование') | ||
ip_address = models.CharField(max_length=255, default="", verbose_name='IP адрес', help_text='192.168.10.10') | ||
port = models.CharField(max_length=5, default="", verbose_name='Порт', help_text='16333') | ||
login = models.CharField(max_length=255, default="", verbose_name='Логин', help_text='login') | ||
password = models.CharField(max_length=255, default="", verbose_name='Пароль', help_text='123456') | ||
hide = models.BooleanField(default=False, blank=True, verbose_name='Скрытие') | ||
address = models.CharField(max_length=128, blank=True, default='', verbose_name="Адрес") | ||
address_fias = models.CharField(max_length=128, blank=True, default=None, null=True, verbose_name="ФИАС Адрес") | ||
address_details = JSONField(blank=True, null=True, verbose_name="Детали адреса") | ||
department = models.ForeignKey(Podrazdeleniya, null=True, db_index=True, verbose_name="Подразделение", on_delete=models.SET_NULL) | ||
location = models.CharField(max_length=255, default="", verbose_name="Местоположение", help_text="2 этаж регистратура") | ||
|
||
class Meta: | ||
verbose_name = "Касса" | ||
verbose_name_plural = "Кассы" | ||
|
||
def __str__(self): | ||
return f"{self.title}" | ||
|
||
def json(self): | ||
return { | ||
"id": self.id, | ||
"label": self.title, | ||
} | ||
|
||
@staticmethod | ||
def get_cash_registers(): | ||
result = [{"id": cash_register.id, "label": cash_register.title} for cash_register in sql_func.get_cash_registers()] | ||
return result | ||
|
||
@staticmethod | ||
def get_meta_data(cash_register_id): | ||
cash_register: CashRegister = CashRegister.objects.get(pk=cash_register_id) | ||
cash_register_data = {"address": cash_register.ip_address, "port": cash_register.port, "login": cash_register.login, "password": cash_register.password} | ||
return cash_register_data | ||
|
||
|
||
class Shift(models.Model): | ||
cash_register = models.ForeignKey(CashRegister, verbose_name='Касса', help_text='Касса 1', null=True, on_delete=models.SET_NULL, db_index=True) | ||
open_at = models.DateTimeField(verbose_name='Время открытия', help_text='2024-07-28 16:00', null=True, blank=True, db_index=True) | ||
close_at = models.DateTimeField(verbose_name='Время закрытия', help_text='2024-07-28 23:00', null=True, blank=True, db_index=True) | ||
operator = models.ForeignKey(DoctorProfile, verbose_name='Оператор', help_text='Сидоров м.п', null=True, on_delete=models.SET_NULL, db_index=True) | ||
open_uuid = models.UUIDField(verbose_name='UUID открытия', help_text='abbfg-45fsd2', null=True, blank=True) | ||
close_uuid = models.UUIDField(verbose_name='UUID Закрытия', help_text='abbfg-45fsd2', null=True, blank=True) | ||
open_status = models.BooleanField(verbose_name='Статус открытия смены', default=False) | ||
close_status = models.BooleanField(verbose_name='Статус открытия смены', default=False) | ||
|
||
class Meta: | ||
verbose_name = "Кассовая смена" | ||
verbose_name_plural = "Кассовые смены" | ||
|
||
def __str__(self): | ||
return f"{self.cash_register.title} - {self.open_at} - {self.close_at} - {self.operator}" | ||
|
||
@staticmethod | ||
def open_shift(uuid_data: str, cash_register_id: int, operator_id: int): | ||
new_shift: Shift = Shift(cash_register_id=cash_register_id, operator_id=operator_id, open_uuid=uuid_data) | ||
new_shift.save() | ||
return {"cash_register_id": new_shift.cash_register_id, "shift_id": new_shift.pk} | ||
|
||
def confirm_open_shift(self): | ||
self.open_status = True | ||
self.open_at = current_time() | ||
self.save() | ||
return True | ||
|
||
@staticmethod | ||
def close_shift(uuid_data: str, cash_register_id: int, operator_id: int): | ||
shift: Shift = Shift.objects.filter(cash_register_id=cash_register_id, operator_id=operator_id, open_status=True, close_status=False).last() | ||
shift.close_uuid = uuid_data | ||
shift.save() | ||
return True | ||
|
||
def confirm_close_shift(self): | ||
self.close_status = True | ||
self.close_at = current_time() | ||
self.save() | ||
return True | ||
|
||
@staticmethod | ||
def get_open_shift_by_operator(operator_id: int): | ||
shift: Shift = Shift.objects.filter(operator_id=operator_id, open_status=True, close_status=False).last() | ||
if shift: | ||
result = {"cash_register_id": shift.cash_register_id, "shift_id": shift.pk} | ||
else: | ||
result = {"cash_register_id": None, "shift_id": None} | ||
return result | ||
|
||
@staticmethod | ||
def check_shift(cash_register_id: int, doctor_profile_id: int): | ||
result_check = sql_func.check_shift(cash_register_id, doctor_profile_id) | ||
if len(result_check) == 0: | ||
result = {"ok": True, "message": ""} | ||
elif result_check[0].operator_id == doctor_profile_id: | ||
result = {"ok": False, "message": "У вас уже есть открытая смена"} | ||
else: | ||
result = {"ok": False, "message": "На этой кассе уже есть открытая смена"} | ||
return result | ||
|
||
@staticmethod | ||
def get_shift_job_data(doctor_profile_id: int, cash_register_id): | ||
operator: DoctorProfile = DoctorProfile.objects.get(pk=doctor_profile_id) | ||
operator_data = {"name": operator.get_full_fio(), "vatin": operator.inn} | ||
cash_register_data = CashRegister.get_meta_data(cash_register_id) | ||
uuid_data = str(uuid.uuid4()) | ||
result = {"operator_data": operator_data, "cash_register_data": cash_register_data, "uuid_data": uuid_data} | ||
return result | ||
|
||
def get_shift_status(self): | ||
uuid_data = None | ||
if not self.open_status and self.open_uuid: | ||
status = "Открывается" | ||
uuid_data = self.open_uuid | ||
elif self.open_status and not self.close_uuid: | ||
status = "Открыта" | ||
else: | ||
status = "Закрывается" | ||
uuid_data = self.close_uuid | ||
return {"status": status, "uuid": uuid_data} | ||
|
||
@staticmethod | ||
def change_status(current_status, job_status, shift): | ||
if current_status == "Открывается": | ||
shift.confirm_open_shift() | ||
result = "Открыта" | ||
else: | ||
shift.confirm_close_shift() | ||
result = "Закрыта" | ||
return result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import requests | ||
from laboratory.settings import CASH_REGISTER_SERVER_ADDRESS, CASH_REGISTER_SERVER_TOKEN | ||
|
||
|
||
def get_authorization_header(): | ||
headers = {"Authorization": f"Bearer {CASH_REGISTER_SERVER_TOKEN}"} | ||
return headers | ||
|
||
|
||
def check_cash_register_status(cash_register_data: dict) -> dict: | ||
headers = get_authorization_header() | ||
body = {"cashRegister": cash_register_data} | ||
try: | ||
response = requests.post(f"{CASH_REGISTER_SERVER_ADDRESS}get-cash-register-status", json=body, headers=headers, timeout=30) | ||
response_data = response.json() | ||
except Exception as e: | ||
return { | ||
"ok": False, | ||
"connection_middle_server_error": True, | ||
"data": f"{e}", | ||
} | ||
return response_data | ||
|
||
|
||
def check_cash_register(cash_register_data: dict): | ||
result = {"ok": True, "message": ""} | ||
cash_register_check = check_cash_register_status(cash_register_data) | ||
if cash_register_check["ok"]: | ||
device_status = cash_register_check["data"]["deviceStatus"] | ||
if not device_status["paperPresent"]: | ||
result = {"ok": False, "message": "В кассе нет бумаги"} | ||
elif device_status["blocked"]: | ||
result = {"ok": False, "message": "Касса заблокирована"} | ||
else: | ||
if cash_register_check.get("connection_middle_server_error"): | ||
result = {"ok": False, "message": "Кассовый middle сервер недоступен"} | ||
elif cash_register_check.get("connectionWebRequestError"): | ||
result = {"ok": False, "message": "Кассовый web-request atol сервер недоступен"} | ||
elif cash_register_check.get("cashRegisterConnectionError"): | ||
# todo - логировать ошибку из ключа "data" | ||
result = {"ok": False, "message": "Ошибка при подключении к кассе"} | ||
return result | ||
|
||
|
||
def send_job(body: dict): | ||
headers = get_authorization_header() | ||
try: | ||
response = requests.post(f"{CASH_REGISTER_SERVER_ADDRESS}push-job", json=body, headers=headers, timeout=5) | ||
response_data = response.json() | ||
except Exception as e: | ||
return {"ok": False, "message": "Ошибка", "data": f"{e}", "connection_middle_server_error": True} | ||
return response_data | ||
|
||
|
||
def get_job_status(uuid: str, cash_register: dict): | ||
body = {"cashRegister": cash_register, "uuid": uuid} | ||
headers = get_authorization_header() | ||
try: | ||
response = requests.post(f"{CASH_REGISTER_SERVER_ADDRESS}get-job-status", json=body, headers=headers, timeout=5) | ||
response_data = response.json() | ||
except Exception as e: | ||
return {"ok": False, "message": "Ошибка", "data": f"{e}", "connection_middle_server_error": True} | ||
return response_data | ||
|
||
|
||
def open_shift(uuid: str, cash_register: dict, operator: dict): | ||
body = {"cashRegister": cash_register, "uuid": uuid, "job": [{"type": "openShift", "operator": operator}]} | ||
result = send_job(body) | ||
return result | ||
|
||
|
||
def close_shift(uuid: str, cash_register: dict, operator: dict): | ||
body = {"cashRegister": cash_register, "uuid": uuid, "job": [{"type": "closeShift", "operator": operator}]} | ||
result = send_job(body) | ||
return result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
from django.db import connection | ||
from utils.db import namedtuplefetchall | ||
|
||
|
||
def get_cash_registers(): | ||
with connection.cursor() as cursor: | ||
cursor.execute( | ||
""" | ||
SELECT * FROM cash_registers_cashregister | ||
""", | ||
) | ||
rows = namedtuplefetchall(cursor) | ||
return rows | ||
|
||
|
||
def check_shift(cash_register_id, doctor_profile_id): | ||
with connection.cursor() as cursor: | ||
cursor.execute( | ||
""" | ||
SELECT | ||
operator_id, | ||
cash_register_id, | ||
close_status | ||
FROM cash_registers_shift | ||
WHERE | ||
(operator_id=%(doctor_profile_id)s or cash_register_id=%(cash_register_id)s) | ||
and | ||
close_status = False | ||
""", | ||
params={"cash_register_id": cash_register_id, "doctor_profile_id": doctor_profile_id}, | ||
) | ||
rows = namedtuplefetchall(cursor) | ||
return rows |
Oops, something went wrong.