mirror of
https://github.com/beak-insights/felicity-lims.git
synced 2025-02-23 16:33:11 +08:00
394 lines
14 KiB
Python
394 lines
14 KiB
Python
import logging
|
|
from datetime import datetime
|
|
|
|
import strawberry # noqa
|
|
from strawberry.types import Info # noqa
|
|
|
|
from felicity.api.gql.auth import auth_from_info
|
|
from felicity.api.gql.billing.types import (AnalysisDiscountType,
|
|
AnalysisPriceType,
|
|
ProfileDiscountType,
|
|
ProfilePriceType,
|
|
TestBillTransactionType,
|
|
TestBillType, VoucherCodeType,
|
|
VoucherType)
|
|
from felicity.api.gql.permissions import IsAuthenticated
|
|
from felicity.api.gql.types import OperationError
|
|
from felicity.apps.billing import schemas, utils
|
|
from felicity.apps.billing.enum import DiscountType, TransactionKind
|
|
from felicity.apps.billing.schemas import (TestBillTransactionUpdate,
|
|
TestBillUpdate)
|
|
from felicity.apps.billing.services import (AnalysisDiscountService,
|
|
AnalysisPriceService,
|
|
ProfileDiscountService,
|
|
ProfilePriceService,
|
|
TestBillService,
|
|
TestBillTransactionService,
|
|
VoucherCodeService, VoucherService)
|
|
from felicity.apps.impress.invoicing import utils as invoice_utils
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
ProfilePriceResponse = strawberry.union(
|
|
"ProfilePriceResponse", (ProfilePriceType, OperationError), description="" # noqa
|
|
)
|
|
|
|
AnalysisPriceResponse = strawberry.union(
|
|
"AnalysisPriceResponse", (AnalysisPriceType, OperationError), description="" #
|
|
)
|
|
|
|
ProfileDiscountResponse = strawberry.union(
|
|
"ProfileDiscountResponse",
|
|
(ProfileDiscountType, OperationError),
|
|
description="", # noqa
|
|
)
|
|
|
|
AnalysisDiscountResponse = strawberry.union(
|
|
"AnalysisDiscountResponse",
|
|
(AnalysisDiscountType, OperationError),
|
|
description="", #
|
|
)
|
|
|
|
VoucherResponse = strawberry.union(
|
|
"VoucherResponse", (VoucherType, OperationError), description="" #
|
|
)
|
|
|
|
VoucherCodeResponse = strawberry.union(
|
|
"VoucherCodeResponse", (VoucherCodeType, OperationError), description="" #
|
|
)
|
|
|
|
TestBillTransactionResponse = strawberry.union(
|
|
"TestBillTransactionResponse",
|
|
(TestBillTransactionType, OperationError),
|
|
description="", #
|
|
)
|
|
|
|
TestBillResponse = strawberry.union(
|
|
"TestBillResponse", (TestBillType, OperationError), description="" #
|
|
)
|
|
|
|
|
|
@strawberry.input
|
|
class PriceInput:
|
|
amount: float
|
|
is_active: bool | None = True
|
|
|
|
|
|
@strawberry.input
|
|
class PriceDiscountInput:
|
|
discount_type: str
|
|
value_type: str | None = None
|
|
start_date: datetime
|
|
end_date: datetime
|
|
voucher_uid: str | None = None
|
|
value_percent: float | None = None
|
|
value_amount: float | None = None
|
|
is_active: bool
|
|
|
|
|
|
@strawberry.input
|
|
class VoucherInput:
|
|
name: str
|
|
usage_limit: int
|
|
start_date: datetime
|
|
end_date: datetime
|
|
once_per_customer: bool
|
|
once_per_order: bool
|
|
|
|
|
|
@strawberry.input
|
|
class VoucherCodeInput:
|
|
code: str
|
|
voucher_uid: str
|
|
usage_limit: int
|
|
is_active: bool
|
|
|
|
|
|
@strawberry.input
|
|
class BillTransactionInput:
|
|
test_bill_uid: str
|
|
kind: str
|
|
amount: float
|
|
notes: str | None = ""
|
|
|
|
|
|
@strawberry.input
|
|
class ApplyVoucherInput:
|
|
voucher_code: str
|
|
test_bill_uid: str
|
|
customer_uid: str
|
|
|
|
|
|
@strawberry.type
|
|
class BillingMutations:
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
async def update_profile_price(
|
|
self, info: Info, uid: str, payload: PriceInput
|
|
) -> ProfilePriceResponse:
|
|
felicity_user = await auth_from_info(info)
|
|
|
|
profile_price = await ProfilePriceService().get(uid=uid)
|
|
incoming: dict = {
|
|
"amount": payload.amount,
|
|
"is_active": payload.is_active,
|
|
"created_by_uid": felicity_user.uid,
|
|
"updated_by_uid": felicity_user.uid,
|
|
}
|
|
obj_in = schemas.ProfilePriceUpdate(**incoming)
|
|
profile_price = await ProfilePriceService().update(profile_price.uid, obj_in)
|
|
return ProfilePriceType(**profile_price.marshal_simple())
|
|
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
async def update_analysis_price(
|
|
self, info: Info, uid: str, payload: PriceInput
|
|
) -> AnalysisPriceResponse:
|
|
felicity_user = await auth_from_info(info)
|
|
|
|
analysis_price = await AnalysisPriceService().get(uid=uid)
|
|
incoming: dict = {
|
|
"amount": payload.amount,
|
|
"is_active": payload.is_active,
|
|
"created_by_uid": felicity_user.uid,
|
|
"updated_by_uid": felicity_user.uid,
|
|
}
|
|
obj_in = schemas.AnalysisPriceUpdate(**incoming)
|
|
analysis_price = await AnalysisPriceService().update(analysis_price.uid, obj_in)
|
|
return AnalysisPriceType(**analysis_price.marshal_simple())
|
|
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
async def update_profile_discount(
|
|
self, info, uid: str, payload: PriceDiscountInput
|
|
) -> ProfileDiscountResponse:
|
|
felicity_user = await auth_from_info(info)
|
|
|
|
if not uid:
|
|
return OperationError(error="No uid provided to identify update obj")
|
|
|
|
profile_discount = await ProfileDiscountService().get(uid=uid)
|
|
if not profile_discount:
|
|
return OperationError(
|
|
error=f"ProfileDiscount with uid {uid} not found. Cannot update obj ..."
|
|
)
|
|
|
|
obj_data = profile_discount.to_dict()
|
|
payload_data = payload.__dict__
|
|
if payload_data["discount_type"] == DiscountType.SALE:
|
|
del payload_data["voucher_uid"]
|
|
|
|
for field in obj_data:
|
|
if field in payload_data:
|
|
try:
|
|
setattr(profile_discount, field, payload_data[field])
|
|
except Exception as e:
|
|
logger.warning(f"failed to set attribute {field}: {e}")
|
|
|
|
update_in: dict = {
|
|
"created_by_uid": felicity_user.uid,
|
|
"updated_by_uid": felicity_user.uid,
|
|
}
|
|
|
|
obj_in = schemas.ProfileDiscountUpdate(
|
|
**{**profile_discount.to_dict(), **update_in}
|
|
)
|
|
profile_discount = await ProfileDiscountService().update(
|
|
profile_discount.uid, obj_in
|
|
)
|
|
return ProfileDiscountType(
|
|
**profile_discount.marshal_simple(),
|
|
)
|
|
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
async def update_analysis_discount(
|
|
self, info, uid: str, payload: PriceDiscountInput
|
|
) -> AnalysisDiscountResponse:
|
|
felicity_user = await auth_from_info(info)
|
|
|
|
if not uid:
|
|
return OperationError(error="No uid provided to identify update obj")
|
|
|
|
analysis_discount = await AnalysisDiscountService().get(uid=uid)
|
|
if not analysis_discount:
|
|
return OperationError(
|
|
error=f"AnalysisDiscount with uid {uid} not found. Cannot update obj ..."
|
|
)
|
|
|
|
obj_data = analysis_discount.to_dict()
|
|
for field in obj_data:
|
|
if field in payload.__dict__:
|
|
try:
|
|
setattr(analysis_discount, field, payload.__dict__[field])
|
|
except Exception as e:
|
|
logger.warning(f"failed to set attribute {field}: {e}")
|
|
|
|
update_in: dict = {
|
|
"created_by_uid": felicity_user.uid,
|
|
"updated_by_uid": felicity_user.uid,
|
|
}
|
|
obj_in = schemas.AnalysisDiscountUpdate(
|
|
**{**analysis_discount.to_dict(), **update_in}
|
|
)
|
|
analysis_discount = await AnalysisDiscountService().update(
|
|
analysis_discount.uid, obj_in
|
|
)
|
|
return AnalysisDiscountType(**analysis_discount.marshal_simple())
|
|
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
async def create_voucher(
|
|
self, info: Info, payload: VoucherInput
|
|
) -> VoucherResponse:
|
|
|
|
felicity_user = await auth_from_info(info)
|
|
|
|
exists = await VoucherService().get(name=payload.name)
|
|
if exists:
|
|
return OperationError(error=f"Voucher {payload.name} already exists")
|
|
|
|
incoming: dict = {
|
|
"created_by_uid": felicity_user.uid,
|
|
"updated_by_uid": felicity_user.uid,
|
|
}
|
|
for k, v in payload.__dict__.items():
|
|
incoming[k] = v
|
|
|
|
obj_in = schemas.VoucherCreate(**incoming)
|
|
voucher = await VoucherService().create(obj_in)
|
|
return VoucherType(**voucher.marshal_simple())
|
|
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
async def update_voucher(
|
|
self, info, uid: str, payload: VoucherInput
|
|
) -> VoucherResponse:
|
|
|
|
felicity_user = await auth_from_info(info)
|
|
|
|
if not uid:
|
|
return OperationError(error="No uid provided to identify update obj")
|
|
|
|
voucher = await VoucherService().get(uid=uid)
|
|
if not voucher:
|
|
return OperationError(
|
|
error=f"Voucher with uid {uid} not found. Cannot update obj ..."
|
|
)
|
|
|
|
obj_data = voucher.to_dict()
|
|
for field in obj_data:
|
|
if field in payload.__dict__:
|
|
try:
|
|
setattr(voucher, field, payload.__dict__[field])
|
|
except Exception as e:
|
|
logger.warning(f"failed to set attribute {field}: {e}")
|
|
obj_in = schemas.VoucherUpdate(**voucher.to_dict())
|
|
voucher = await VoucherService().update(voucher.uid, obj_in)
|
|
return VoucherType(**voucher.marshal_simple())
|
|
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
async def create_voucher_code(
|
|
self, info: Info, payload: VoucherCodeInput
|
|
) -> VoucherCodeResponse:
|
|
|
|
felicity_user = await auth_from_info(info)
|
|
|
|
exists = await VoucherCodeService().get(code=payload.code)
|
|
if exists:
|
|
return OperationError(error=f"Voucher Code {payload.code} already exists")
|
|
|
|
incoming: dict = {
|
|
"created_by_uid": felicity_user.uid,
|
|
"updated_by_uid": felicity_user.uid,
|
|
}
|
|
for k, v in payload.__dict__.items():
|
|
incoming[k] = v
|
|
|
|
obj_in = schemas.VoucherCodeCreate(**incoming)
|
|
voucher_code = await VoucherCodeService().create(obj_in)
|
|
return VoucherCodeType(**voucher_code.marshal_simple())
|
|
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
async def update_voucher_code(
|
|
self, info, uid: str, payload: VoucherCodeInput
|
|
) -> VoucherCodeResponse:
|
|
|
|
felicity_user = await auth_from_info(info)
|
|
|
|
if not uid:
|
|
return OperationError(error="No uid provided to identify update obj")
|
|
|
|
voucher_code = await VoucherCodeService().get(uid=uid)
|
|
if not voucher_code:
|
|
return OperationError(
|
|
error=f"Voucher with uid {uid} not found. Cannot update obj ..."
|
|
)
|
|
|
|
obj_data = voucher_code.to_dict()
|
|
for field in obj_data:
|
|
if field in payload.__dict__:
|
|
try:
|
|
setattr(voucher_code, field, payload.__dict__[field])
|
|
except Exception as e:
|
|
logger.warning(f"failed to set attribute {field}: {e}")
|
|
obj_in = schemas.VoucherCodeUpdate(**voucher_code.to_dict())
|
|
voucher_code = await VoucherCodeService().update(voucher_code.uid, obj_in)
|
|
return VoucherCodeType(**voucher_code.marshal_simple())
|
|
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
async def create_test_bill_transaction(
|
|
self, info, payload: BillTransactionInput
|
|
) -> TestBillTransactionResponse:
|
|
|
|
felicity_user = await auth_from_info(info)
|
|
if payload.amount <= 0:
|
|
return OperationError(
|
|
error="Invalid transaction Amount.",
|
|
suggestion="Transaction amount must be greater than 0",
|
|
)
|
|
|
|
test_bill = await TestBillService().get(uid=payload.test_bill_uid)
|
|
incoming: dict = {
|
|
"patient_uid": test_bill.patient_uid,
|
|
"client_uid": test_bill.client_uid,
|
|
"created_by_uid": felicity_user.uid,
|
|
"updated_by_uid": felicity_user.uid,
|
|
}
|
|
|
|
for k, v in payload.__dict__.items():
|
|
incoming[k] = v
|
|
|
|
obj_in = schemas.TestBillTransactionCreate(**incoming)
|
|
tbt = await TestBillTransactionService().create(obj_in)
|
|
|
|
test_bill_update = {
|
|
"total_paid": test_bill.total_paid + tbt.amount,
|
|
"partial": True,
|
|
}
|
|
if test_bill_update["total_paid"] >= test_bill.total_charged:
|
|
test_bill_update["partial"] = False
|
|
test_bill_update["is_active"] = False
|
|
await TestBillService().update(
|
|
test_bill.uid, TestBillUpdate(**test_bill_update)
|
|
)
|
|
|
|
transaction_update = {"is_success": True}
|
|
if payload.kind == TransactionKind.CASH:
|
|
transaction_update["action_required"] = False
|
|
transaction_update["processed"] = True
|
|
else:
|
|
incoming["action_required"] = False
|
|
incoming["action_message"] = "Confirm funds reception"
|
|
|
|
tbt = await TestBillTransactionService().update(
|
|
tbt.uid, TestBillTransactionUpdate(**transaction_update)
|
|
)
|
|
await invoice_utils.impress_invoice(test_bill)
|
|
return TestBillTransactionType(**tbt.marshal_simple())
|
|
|
|
@strawberry.mutation(permission_classes=[IsAuthenticated])
|
|
async def apply_voucher(
|
|
self, info, payload: ApplyVoucherInput
|
|
) -> TestBillTransactionResponse:
|
|
|
|
felicity_user = await auth_from_info(info)
|
|
bill = await utils.apply_voucher(
|
|
payload.voucher_code, payload.test_bill_uid, payload.customer_uid
|
|
)
|
|
return TestBillType(**bill.marshal_simple(exclude=["orders"]))
|