backend.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. #!/usr/bin/env python3
  2. from typing import List, Tuple, ClassVar, Type, Any
  3. from dataclasses import dataclass
  4. from marshmallow import Schema
  5. import xmlrpc.client
  6. import marshmallow_dataclass
  7. import pprint
  8. try:
  9. from types import SimpleNamespace as Namespace
  10. except ImportError:
  11. # Python 2.x fallback
  12. from argparse import Namespace
  13. @dataclass
  14. class Address:
  15. """All of the Dataclasses us Marshmallow for Serialisation
  16. This is the adress. It contains coordinates but also the adress"""
  17. id: int
  18. name: str
  19. phone: str
  20. street: str
  21. street2: str
  22. city: str
  23. zip: str
  24. longitude: float
  25. latitude: float
  26. # state: str
  27. @dataclass
  28. class Sale:
  29. """Unlike pythons dynamic Typing we use statically Typed Classes
  30. This allows us to catch TypeErrors and thus allows us to validate the
  31. Answer from the Server"""
  32. id: int
  33. name: str
  34. invoice_status: str
  35. amount_total: float
  36. amount_tax: float
  37. create_date: str
  38. order_line: List[int]
  39. # price_subtotal: float
  40. @dataclass
  41. class Sale_Line:
  42. """This class is one line of a sail.
  43. The relation Ship is many sale_lines to one sale"""
  44. id: int
  45. name: str
  46. price_total: float
  47. product_uom_qty: int
  48. @dataclass
  49. class Picking(object):
  50. """Pickings are drone flights which are created by Sales
  51. A Sale has three steps in a picking. It is picked, packed and then shiped
  52. currently a user in the Backend advances the picking in the web app.
  53. picking validation threw GPS or QR Codes is a feature automation
  54. """
  55. id: int
  56. name: str
  57. group_id: Tuple[int, str]
  58. location_id: Tuple[int, str]
  59. location_dest_id: Tuple[int, str]
  60. move_lines: List[int]
  61. partner_id: Tuple[int, str]
  62. sale_id: Tuple[int, str]
  63. state: str
  64. create_date: str
  65. printed: bool
  66. product_id: Tuple[int, str]
  67. # wk_picking_notes: Any
  68. Schema: ClassVar[Type[Schema]] = Schema
  69. PickingSchema = marshmallow_dataclass.class_schema(Picking)
  70. AddressSchema = marshmallow_dataclass.class_schema(Address)
  71. SaleLineSchema = marshmallow_dataclass.class_schema(Sale_Line)
  72. SaleSchema = marshmallow_dataclass.class_schema(Sale)
  73. class API:
  74. """This interacts with the external odoo 13 API with XMLrpc
  75. The API Class is intended to be used a black box implentation
  76. which is imported by another Class or Python Scipt
  77. """
  78. url = "http://localhost:8069"
  79. db = "anemoi_test"
  80. username = "test" # FIXME Read Val from system env or file
  81. password = "1234" # FIXME Read Val from system env or file
  82. common = xmlrpc.client.ServerProxy("{}/xmlrpc/2/common".format(url))
  83. models = xmlrpc.client.ServerProxy("{}/xmlrpc/2/object".format(url))
  84. uid = common.authenticate(db, username, password, {})
  85. def __init__(
  86. self, id, name, number, addres, description, menge, preis, sum_order, comment,
  87. ):
  88. """Reads the values from its parameters and uses them to build the API"""
  89. self.printed = True
  90. self.id = id
  91. self.name = name
  92. self.number = number
  93. self.addres = addres
  94. self.description = description
  95. self.menge = menge
  96. self.preis = preis
  97. self.sum_order = sum_order
  98. self.comment = comment
  99. def login():
  100. """Login into the Server return the reading rights of stock.picking"""
  101. # To verify if the connection information is correct before trying to authenticate,
  102. # the simplest call is to ask for the server’s version.
  103. print(API.common.version())
  104. # The second endpoint is xmlrpc/2/object, is used to call
  105. # methods of odoo models via the execute_kw RPC function.
  106. return API.models.execute_kw(
  107. API.db,
  108. API.uid,
  109. API.password,
  110. "stock.picking",
  111. "check_access_rights",
  112. ["read"],
  113. {"raise_exception": False},
  114. )
  115. def get_fields(field_name):
  116. """What Fields does the our Odoo Model have of of this data Field"""
  117. return API.models.execute_kw(
  118. API.db,
  119. API.uid,
  120. API.password,
  121. field_name,
  122. "fields_get",
  123. [],
  124. {"attributes": ["string", "help", "type"]},
  125. )
  126. def get_partner_address(ids):
  127. """Returns the adress and geolocation of all ids as a AdressSchema"""
  128. ps = API.models.execute_kw(
  129. API.db,
  130. API.uid,
  131. API.password,
  132. "res.partner",
  133. "read",
  134. [ids],
  135. {
  136. "fields": [
  137. "name",
  138. "street",
  139. "street2",
  140. "zip",
  141. "phone",
  142. "city",
  143. "partner_latitude",
  144. "partner_longitude",
  145. ]
  146. },
  147. )
  148. p_objects = [AddressSchema().load(p) for p in ps]
  149. return p_objects
  150. def get_not_printed_pickings():
  151. """Returns all Pickings which have not been not been printed or processed
  152. The xml Response of the Server is parsed to a python Data Picking Class """
  153. ps = API.models.execute_kw(
  154. API.db,
  155. API.uid,
  156. API.password,
  157. "stock.picking",
  158. "search_read",
  159. [
  160. ["&", ("printed", "!=", True), ("state", "!=", "cancel")]
  161. ], # TODO add check if adress is null.
  162. {
  163. "fields": [
  164. "name",
  165. "partner_id",
  166. "sale_id",
  167. "state",
  168. "create_date",
  169. "printed",
  170. "product_id",
  171. "move_lines",
  172. "location_id",
  173. "location_dest_id",
  174. "group_id",
  175. ],
  176. "limit": 50,
  177. },
  178. )
  179. pprint.pprint(ps)
  180. p_objects = [PickingSchema().load(p) for p in ps]
  181. return p_objects
  182. def __load_picking(p_picking):
  183. """Loads the Picking Schema from a String. Usefull for testing"""
  184. picking_schema = marshmallow_dataclass.class_schema(Picking)
  185. return picking_schema.load(p_picking)
  186. def get_product(ids):
  187. """Returns all orders Lines of a Product"""
  188. return API.models.execute_kw(
  189. API.db,
  190. API.uid,
  191. API.password,
  192. "product.product",
  193. "read",
  194. [ids],
  195. {"fields": ["order_line",]},
  196. )
  197. def post_printed_picking(printed: bool, picking_id: int):
  198. """Post the new value of printed for picking_id"""
  199. API.models.execute_kw(
  200. API.db,
  201. API.uid,
  202. API.password,
  203. "stock.picking",
  204. "write",
  205. [[picking_id], {"printed": printed}],
  206. )