Antoni09 commited on
Commit
a0c3c6b
·
1 Parent(s): e69ba0c

Fix paid personal invoice client storage

Browse files
Files changed (2) hide show
  1. db.py +50 -33
  2. server.py +13 -5
db.py CHANGED
@@ -1,11 +1,13 @@
1
- import os
2
- from contextlib import contextmanager
3
- from typing import Any, Dict, List, Optional, Sequence
 
4
 
5
  import psycopg2
6
  from psycopg2.extras import RealDictCursor
7
 
8
- DATABASE_URL = os.environ.get("NEON_DATABASE_URL")
 
9
 
10
  if not DATABASE_URL:
11
  raise RuntimeError(
@@ -125,37 +127,50 @@ def update_business_logo(account_id: int, mime: Optional[str], data_base64: Opti
125
 
126
  def upsert_client(account_id: int, payload: Dict[str, str]) -> int:
127
  tax_id = (payload.get("tax_id") or "").strip()
128
- stored_tax_id = tax_id or None
129
  if tax_id:
130
- row = fetch_one(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  """
132
- SELECT id FROM clients
133
- WHERE account_id = %s AND tax_id = %s
 
 
 
 
 
134
  """,
135
- (account_id, tax_id),
 
 
 
 
 
 
 
136
  )
137
- if row:
138
- client_id = row["id"]
139
- execute(
140
- """
141
- UPDATE clients
142
- SET name = %s,
143
- address_line = %s,
144
- postal_code = %s,
145
- city = %s,
146
- phone = %s
147
- WHERE id = %s
148
- """,
149
- (
150
- payload["name"],
151
- payload["address_line"],
152
- payload["postal_code"],
153
- payload["city"],
154
- payload.get("phone"),
155
- client_id,
156
- ),
157
- )
158
- return client_id
159
 
160
  with db_conn() as conn, conn.cursor() as cur:
161
  cur.execute(
@@ -182,7 +197,9 @@ def search_clients(account_id: int, term: str, limit: int = 10) -> List[Dict[str
182
  like = f"%{query}%"
183
  return fetch_all(
184
  """
185
- SELECT name, tax_id, address_line, postal_code, city, phone
 
 
186
  FROM clients
187
  WHERE account_id = %s
188
  AND (
@@ -193,7 +210,7 @@ def search_clients(account_id: int, term: str, limit: int = 10) -> List[Dict[str
193
  ORDER BY LOWER(COALESCE(name, tax_id, '')) ASC
194
  LIMIT %s
195
  """,
196
- (account_id, query, like, like, limit),
197
  )
198
 
199
 
 
1
+ import os
2
+ import hashlib
3
+ from contextlib import contextmanager
4
+ from typing import Any, Dict, List, Optional, Sequence
5
 
6
  import psycopg2
7
  from psycopg2.extras import RealDictCursor
8
 
9
+ DATABASE_URL = os.environ.get("NEON_DATABASE_URL")
10
+ PRIVATE_TAX_ID_PREFIX = "__PRIVATE__:"
11
 
12
  if not DATABASE_URL:
13
  raise RuntimeError(
 
127
 
128
  def upsert_client(account_id: int, payload: Dict[str, str]) -> int:
129
  tax_id = (payload.get("tax_id") or "").strip()
 
130
  if tax_id:
131
+ stored_tax_id = tax_id
132
+ else:
133
+ identity = "|".join(
134
+ [
135
+ str(account_id),
136
+ (payload.get("name") or "").strip().lower(),
137
+ (payload.get("address_line") or "").strip().lower(),
138
+ (payload.get("postal_code") or "").strip().lower(),
139
+ (payload.get("city") or "").strip().lower(),
140
+ (payload.get("phone") or "").strip().lower(),
141
+ ]
142
+ )
143
+ stored_tax_id = f"{PRIVATE_TAX_ID_PREFIX}{hashlib.sha1(identity.encode('utf-8')).hexdigest()[:20]}"
144
+
145
+ row = fetch_one(
146
+ """
147
+ SELECT id FROM clients
148
+ WHERE account_id = %s AND tax_id = %s
149
+ """,
150
+ (account_id, stored_tax_id),
151
+ )
152
+ if row:
153
+ client_id = row["id"]
154
+ execute(
155
  """
156
+ UPDATE clients
157
+ SET name = %s,
158
+ address_line = %s,
159
+ postal_code = %s,
160
+ city = %s,
161
+ phone = %s
162
+ WHERE id = %s
163
  """,
164
+ (
165
+ payload["name"],
166
+ payload["address_line"],
167
+ payload["postal_code"],
168
+ payload["city"],
169
+ payload.get("phone"),
170
+ client_id,
171
+ ),
172
  )
173
+ return client_id
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
 
175
  with db_conn() as conn, conn.cursor() as cur:
176
  cur.execute(
 
197
  like = f"%{query}%"
198
  return fetch_all(
199
  """
200
+ SELECT name,
201
+ CASE WHEN tax_id LIKE %s THEN '' ELSE tax_id END AS tax_id,
202
+ address_line, postal_code, city, phone
203
  FROM clients
204
  WHERE account_id = %s
205
  AND (
 
210
  ORDER BY LOWER(COALESCE(name, tax_id, '')) ASC
211
  LIMIT %s
212
  """,
213
+ (f"{PRIVATE_TAX_ID_PREFIX}%", account_id, query, like, like, limit),
214
  )
215
 
216
 
server.py CHANGED
@@ -31,7 +31,8 @@ DATA_FILE = DATA_DIR / "web_invoice_store.json"
31
  INVOICE_HISTORY_LIMIT = 200
32
  MAX_LOGO_SIZE = 512 * 1024 # 512 KB
33
  TOKEN_TTL = timedelta(hours=12)
34
- ALLOWED_LOGO_MIME_TYPES = {"image/png", "image/jpeg"}
 
35
 
36
  DATABASE_AVAILABLE = bool(os.environ.get("NEON_DATABASE_URL"))
37
 
@@ -503,11 +504,18 @@ def api_logo() -> Any:
503
  save_store(data)
504
  return jsonify({"logo": stored_logo})
505
 
506
- def normalize_phone(phone: Optional[str]) -> Optional[str]:
507
  if not phone:
508
  return None
509
- digits = re.sub(r"[^\d+]", "", phone)
510
- return digits or None
 
 
 
 
 
 
 
511
 
512
 
513
  def validate_client(payload: Dict[str, Any]) -> Dict[str, str]:
@@ -750,7 +758,7 @@ def api_invoices() -> Any:
750
  "address_line": row.get("client_address"),
751
  "postal_code": row.get("client_postal_code"),
752
  "city": row.get("client_city"),
753
- "tax_id": row.get("client_tax_id"),
754
  "phone": row.get("client_phone"),
755
  }
756
  invoices.append(
 
31
  INVOICE_HISTORY_LIMIT = 200
32
  MAX_LOGO_SIZE = 512 * 1024 # 512 KB
33
  TOKEN_TTL = timedelta(hours=12)
34
+ ALLOWED_LOGO_MIME_TYPES = {"image/png", "image/jpeg"}
35
+ PRIVATE_TAX_ID_PREFIX = "__PRIVATE__:"
36
 
37
  DATABASE_AVAILABLE = bool(os.environ.get("NEON_DATABASE_URL"))
38
 
 
504
  save_store(data)
505
  return jsonify({"logo": stored_logo})
506
 
507
+ def normalize_phone(phone: Optional[str]) -> Optional[str]:
508
  if not phone:
509
  return None
510
+ digits = re.sub(r"[^\d+]", "", phone)
511
+ return digits or None
512
+
513
+
514
+ def public_tax_id(value: Optional[str]) -> str:
515
+ tax_id = (value or "").strip()
516
+ if tax_id.startswith(PRIVATE_TAX_ID_PREFIX):
517
+ return ""
518
+ return tax_id
519
 
520
 
521
  def validate_client(payload: Dict[str, Any]) -> Dict[str, str]:
 
758
  "address_line": row.get("client_address"),
759
  "postal_code": row.get("client_postal_code"),
760
  "city": row.get("client_city"),
761
+ "tax_id": public_tax_id(row.get("client_tax_id")),
762
  "phone": row.get("client_phone"),
763
  }
764
  invoices.append(