Verified Commit 8ca910c9 authored by Sli's avatar Sli
Browse files

server: implements money with strings

parent e7f33f50
......@@ -24,23 +24,37 @@ enum PaymentMethod {
OTHER = 6;
}
// This is based on the DecimalTuple format available on Python
message Money {
// 0 if positive and 1 if negative
int32 sign = 1;
// Exponent to multiply the total number by, if you have 1.17 exponent is -2
int64 exponent = 2;
// Individual digits of the number
// Example: for 1.17 it would be [1, 1, 7]
repeated uint32 digits = 3;
}
message Product {
message HappyHour {
google.protobuf.Timestamp start = 1;
google.protobuf.Timestamp end = 2;
double price = 3;
Money price = 3;
}
int64 id = 1;
string name = 2; //pretty print in trees
string code = 3; //code for fast search, not necessarily unique
float default_price = 4;
Money default_price = 4;
repeated HappyHour happy_hours = 5;
}
message BasketItem {
int64 product_id = 1;
int64 quantity = 2;
double unit_price = 3; // Price at buying time, used in History, not mandatory when it's a BuyingRequest
Money unit_price = 3; // Price at buying time, used in History, not mandatory when it's a BuyingRequest
}
message Refilling {
......@@ -48,19 +62,19 @@ message Refilling {
int64 counter_id = 2;
string device_uuid = 3;
PaymentMethod payment_method = 4;
double amount = 5;
Money amount = 5;
google.protobuf.Timestamp date = 6;
}
message Payment { //Allow us to handle multiple user buyings, describe who pays and how much.
string customer_id = 1;
double amount = 2;
Money amount = 2;
}
message Buying {
int64 id = 1;
string label = 2; // description of the buying, ui purpose only
double price = 3; // price the customer(s) actually paid at 'date', sum of Payments
Money price = 3; // price the customer(s) actually paid at 'date', sum of Payments
bool refounded = 4;
int64 counter_id = 5;
google.protobuf.Timestamp date = 6;
......@@ -96,7 +110,7 @@ message BuyingReply {
google.protobuf.Timestamp now = 2;
Buying transaction = 3;
repeated string customer_ids = 4; //Severeal
repeated double customer_balances = 5;
repeated Money customer_balances = 5;
}
message RefillingRequest {
......@@ -104,7 +118,7 @@ message RefillingRequest {
int64 counter_id = 2;
string device_uuid = 3;
PaymentMethod payment_method = 4;
double amount = 5;
Money amount = 5;
}
message RefillingReply {
......@@ -125,7 +139,7 @@ message RefillingReply {
Status status = 1;
google.protobuf.Timestamp now = 2;
double customer_balance = 3;
Money customer_balance = 3;
}
message RefoundBuyingRequest {
......@@ -149,8 +163,8 @@ message RefoundBuyingReply {
Status status = 1;
google.protobuf.Timestamp now = 2;
repeated double customer_id = 3; // Multiple customer could be refounded at the same time since multi-payment exist
repeated double customer_balance = 4; //So we can have multiple new balances...
repeated Money customer_id = 3; // Multiple customer could be refounded at the same time since multi-payment exist
repeated Money customer_balance = 4; //So we can have multiple new balances...
}
message CancelRefillingRequest {
......@@ -174,14 +188,14 @@ message CancelRefillingReply {
Status status = 1;
google.protobuf.Timestamp now = 2;
double customer_id = 3;
double customer_balance = 4;
Money customer_id = 3;
Money customer_balance = 4;
}
message TransfertRequest {
string origin_id = 1;
string destination_id = 2;
double amount = 3;
Money amount = 3;
int64 counter_id = 4;
string device_uuid = 5;
}
......@@ -203,8 +217,8 @@ message TransfertReply {
}
Status status = 1;
google.protobuf.Timestamp now = 2;
double origin_balance = 3;
double destination_balance = 4;
Money origin_balance = 3;
Money destination_balance = 4;
}
message BalanceRequest {
......@@ -220,7 +234,7 @@ message BalanceReply {
}
Status status = 1;
google.protobuf.Timestamp now = 2;
double balance = 3;
Money balance = 3;
}
message HistoryRequest {
......
grpcio==1.29
grpcio-tools==1.29
grpcio-reflection=1.29
grpcio-reflection==1.29
SQLAlchemy==1.3.17
click==7.1.2
jsonschema
......
# -*- coding:utf-8 -*
from datetime import datetime
import decimal
from sqlalchemy import (
Column,
Integer,
String,
Float,
Boolean,
DateTime,
ForeignKey,
)
from sqlalchemy.orm import relationship, backref
import sqlalchemy.types as types
from server import Model
class Money(types.TypeDecorator):
"""
Store fixed decimal values as strings
"""
impl = types.String
def process_bind_param(self, value, dialect):
return str(value)
def process_result_value(self, value, dialect):
return decimal.Decimal(value)
class Customer(Model):
"""
Customer class
......@@ -23,7 +38,7 @@ class Customer(Model):
__tablename__ = "customers"
id = Column(String, primary_key=True, unique=True, autoincrement=False)
balance = Column(Float)
balance = Column(Money)
class Machine(Model):
......@@ -58,7 +73,7 @@ class Product(Model):
name = Column(String) # Real fancy name
code = Column(String) # Slugified name
category = Column(String) # Dot separated string for the category main.sub.category
default_price = Column(Float)
default_price = Column(Money)
class HappyHour(Model):
......@@ -72,7 +87,7 @@ class HappyHour(Model):
product = relationship(
"Product", backref=backref("happy_hours", lazy=True), lazy=True
)
price = Column(Float)
price = Column(Money)
start = Column(DateTime(timezone=True))
end = Column(DateTime(timezone=True))
......@@ -134,7 +149,7 @@ class Refilling(Model):
"Machine", backref=backref("refillings", lazy=True), lazy=True
)
amount = Column(Float)
amount = Column(Money)
cancelled = Column(Boolean, default=False)
......@@ -177,7 +192,7 @@ class BasketItem(Model):
)
quantity = Column(Integer)
unit_price = Column(Float) # Unit price of the product at the time of purchase
unit_price = Column(Money) # Unit price of the product at the time of purchase
class Payment(Model):
......@@ -196,4 +211,4 @@ class Payment(Model):
buying_id = Column(Integer, ForeignKey("buyings.id"))
buying = relationship("Buying", backref=backref("payments", lazy=True), lazy=True)
amount = Column(Float) # Amount spent by this user in the Buying
amount = Column(Money) # Amount spent by this user in the Buying
......@@ -3,6 +3,7 @@
import grpc
import json
import decimal
from concurrent import futures
from google.protobuf.timestamp_pb2 import Timestamp
......@@ -11,12 +12,31 @@ from google.protobuf.json_format import Parse
from server import db, models, com_pb2, com_pb2_grpc
def pb_now():
def pb_now() -> Timestamp:
timestamp = Timestamp()
timestamp.GetCurrentTime()
return timestamp
def decimal_to_pb_money(dec: decimal.Decimal) -> com_pb2.Money:
tup = dec.as_tuple()
return com_pb2.Money(sign=tup.sign, exponent=tup.exponent, digits=tup.digits)
def decimal_to_pb_money_dict(dec: decimal.Decimal):
tup = dec.as_tuple()
print(dec, tup)
return {"sign": tup.sign, "exponent": tup.exponent, "digits": list(tup.digits)}
def pb_money_to_decimal(money: com_pb2.Money) -> decimal.Decimal:
return decimal.Decimal(
decimal.DecimalTuple(
sign=money.sign, digits=money.digits, exponent=money.exponent
)
)
class PaymentServicer(com_pb2_grpc.PaymentProtocolServicer):
"""
Communication protocol to communicate with the cashless client
......@@ -67,7 +87,9 @@ class PaymentServicer(com_pb2_grpc.PaymentProtocolServicer):
db.commit()
return com_pb2.BalanceReply(
status=com_pb2.BalanceReply.SUCCESS, now=pb_now(), balance=customer.balance
status=com_pb2.BalanceReply.SUCCESS,
now=pb_now(),
balance=decimal_to_pb_money(customer.balance),
)
def History(self, request, context):
......@@ -123,7 +145,7 @@ class PaymentServicer(com_pb2_grpc.PaymentProtocolServicer):
"id": product.id,
"name": product.name,
"code": product.code,
"default_price": product.default_price,
"default_price": decimal_to_pb_money_dict(product.default_price),
"happy_hours": [],
}
......@@ -137,7 +159,7 @@ class PaymentServicer(com_pb2_grpc.PaymentProtocolServicer):
{
"start": start.ToJsonString(),
"end": end.ToJsonString(),
"price": happy_hour.price,
"price": decimal_to_pb_money_dict(happy_hour.price),
}
)
pos["products"].append(p)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment