Doc, tools for lokavaluto development
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

460 lines
18 KiB

  1. # coding: utf-8
  2. from __future__ import unicode_literals
  3. import os
  4. import argparse
  5. import base64
  6. import logging
  7. import requests
  8. import yaml # PyYAML
  9. from datetime import datetime
  10. from datetime import timedelta
  11. logging.basicConfig()
  12. logger = logging.getLogger(__name__)
  13. def check_request_status(r):
  14. if r.status_code == requests.codes.ok:
  15. logger.info('OK')
  16. else:
  17. logger.error(r.text)
  18. r.raise_for_status()
  19. # Ensemble des constantes nécessaires au fonctionnement du script.
  20. ENV = os.environ.get('ENV')
  21. LOCAL_CURRENCY_INTERNAL_NAME = os.environ.get('CURRENCY_SLUG')
  22. NETWORK_INTERNAL_NAME = LOCAL_CURRENCY_INTERNAL_NAME
  23. if ENV != 'prod':
  24. NETWORK_INTERNAL_NAME = ENV + LOCAL_CURRENCY_INTERNAL_NAME
  25. LOCAL_CURRENCY_SYMBOL = os.environ.get('CURRENCY_SYMBOL')
  26. # Arguments à fournir dans la ligne de commande
  27. parser = argparse.ArgumentParser()
  28. parser.add_argument('url', help='URL of Cyclos')
  29. parser.add_argument('authorization',
  30. help='string to use for Basic Authentication')
  31. parser.add_argument('--debug',
  32. help='enable debug messages',
  33. action='store_true')
  34. args = parser.parse_args()
  35. if not args.url.endswith('/'):
  36. args.url = args.url + '/'
  37. if args.debug:
  38. logger.setLevel(logging.DEBUG)
  39. else:
  40. logger.setLevel(logging.INFO)
  41. for k, v in vars(args).items():
  42. logger.debug('args.%s = %s', k, v)
  43. # URLs des web services
  44. global_web_services = args.url + 'global/web-rpc/'
  45. network_web_services = args.url + '/' + NETWORK_INTERNAL_NAME + '/web-rpc/'
  46. # En-têtes pour toutes les requêtes (il n'y a qu'un en-tête, pour
  47. # l'authentification).
  48. headers = {'Authorization': 'Basic ' + args.authorization}
  49. # On fait une 1ère requête en lecture seule uniquement pour vérifier
  50. # si les paramètres fournis sont corrects.
  51. logger.info('Vérification des paramètres fournis...')
  52. r = requests.post(global_web_services + 'network/search',
  53. headers=headers, json={})
  54. check_request_status(r)
  55. ########################################################################
  56. # Création des utilisateurs pour les banques de dépôt et les comptes
  57. # dédiés.
  58. #
  59. def create_user(group, name, login, email=None, password=None,city=None, custom_values=None):
  60. logger.info('Création de l\'utilisateur "%s" (groupe "%s")...', name, group)
  61. # FIXME code à déplacer pour ne pas l'exécuter à chaque fois
  62. r = requests.post(network_web_services + 'group/search',
  63. headers=headers, json={})
  64. check_request_status(r)
  65. groups = r.json()['result']['pageItems']
  66. for g in groups:
  67. if g['name'] == group:
  68. group_id = g['id']
  69. if not city:
  70. city = 'Grenoble'
  71. user_registration = {
  72. 'group': group_id,
  73. 'name': name,
  74. 'username': login,
  75. 'skipActivationEmail': True,
  76. 'addresses':[
  77. {
  78. 'name': 'work',
  79. 'addressLine1': '10 rue du test',
  80. 'city': city,
  81. 'defaultAddress': True,
  82. 'hidden': False
  83. }
  84. ]
  85. }
  86. if email:
  87. user_registration['email'] = email
  88. if password:
  89. user_registration['passwords'] = [
  90. {
  91. 'type': 'login',
  92. 'value': password,
  93. 'confirmationValue': password,
  94. 'assign': True,
  95. 'forceChange': False,
  96. },
  97. ]
  98. if custom_values:
  99. user_registration['customValues'] = []
  100. for field_id, value in custom_values.items():
  101. r = requests.get(network_web_services + 'userCustomField/load/' + field_id, headers=headers)
  102. check_request_status(r)
  103. custom_field_type = r.json()['result']['type']
  104. if custom_field_type == 'LINKED_ENTITY':
  105. value_key = 'linkedEntityValue'
  106. user_registration['customValues'].append({
  107. 'field': field_id,
  108. value_key: value,
  109. })
  110. logger.debug('create_user : json = %s', user_registration)
  111. r = requests.post(network_web_services + 'user/register',
  112. headers=headers,
  113. json=user_registration)
  114. check_request_status(r)
  115. logger.debug('result = %s', r.json()['result'])
  116. user_id = r.json()['result']['user']['id']
  117. logger.debug('user_id = %s', user_id)
  118. return user_id
  119. create_user(
  120. group='Network administrators',
  121. name= 'Administrator',
  122. login= 'admin_network',
  123. email= 'administrator@localhost.fr',
  124. password= '@@bbccdd' #@WARNING : nécessité d'avoir ce mot de passe pour les tests appli CEL du Cairn
  125. )
  126. create_user(
  127. group='Banques de dépôt',
  128. name='Banque de dépôt 1',
  129. login='BDP1',
  130. )
  131. create_user(
  132. group='Banques de dépôt',
  133. name='Banque de dépôt 2',
  134. login='BDP2',
  135. )
  136. ########################################################################
  137. # Création des utilisateurs pour les tests.
  138. if ENV != 'prod':
  139. # On récupère l'id du champ perso 'BDC'.
  140. r = requests.get(network_web_services + 'userCustomField/list', headers=headers)
  141. check_request_status(r)
  142. user_custom_fields = r.json()['result']
  143. for field in user_custom_fields:
  144. if field['internalName'] == 'bdc':
  145. id_user_custom_field_bdc = field['id']
  146. gestion_interne = {
  147. 'demo': 'Demo',
  148. 'demo2': 'Demo2',
  149. }
  150. for login, name in gestion_interne.items():
  151. create_user(
  152. group='Gestion interne',
  153. name=name,
  154. login=login,
  155. password=login,
  156. )
  157. bureaux_de_change = {
  158. 'B000': 'Association Monnaie Locale',
  159. 'B001': 'Bureau de change 1',
  160. 'B002': 'Bureau de change 2',
  161. }
  162. for login, name in bureaux_de_change.items():
  163. id_bdc = create_user(
  164. group='Bureaux de change',
  165. name=name + ' (BDC)',
  166. login=login + '_BDC',
  167. password=login,
  168. )
  169. create_user(
  170. group='Opérateurs BDC',
  171. name=name,
  172. login=login,
  173. password=login,
  174. custom_values={
  175. id_user_custom_field_bdc: id_bdc,
  176. }
  177. )
  178. create_user(
  179. group='Anonyme',
  180. name='Anonyme',
  181. login='anonyme',
  182. password='anonyme',
  183. )
  184. adherents_utilisateurs = [
  185. ['gjanssens', 'Janssens Gaetan'],
  186. ['cretine_agnes', 'Créttine Agnès'],
  187. ['alberto_malik', 'Malik Alberto'],
  188. ['noire_aliss', 'La noire Aliss'],
  189. ['tous_andre', 'Tous Ensemble André'],
  190. ['speedy_andrew', 'Speedy Andrew'],
  191. ['stuart_andrew', 'Stuart Andrew'],
  192. ['crabe_arnold', 'Le Crabe Arnold'],
  193. ['barbare_cohen', 'le Barbare Cohen'],
  194. ['lacreuse_desiderata', 'Lacreuse Desiderata'],
  195. ['comblant_michel', 'Comblant Michel'],
  196. ['nico_faus_perso', 'Le Caméléon Nicolas'],
  197. ['benoit_perso', 'Le Rigolo Benoît'],
  198. ]
  199. adherents_prestataires = [
  200. ['asso_mlc', 'Association Monnaie Locale','Grenoble'],
  201. ['maltobar', 'MaltOBar','Grenoble'],
  202. ['labonnepioche', 'La Bonne Pioche','Grenoble'],
  203. ['DrDBrew', 'DocteurD Brew Pub','Grenoble'],
  204. ['apogee_du_vin', 'Apogée du vin','Grenoble'],
  205. ['tout_1_fromage', 'Tout un fromage','Grenoble'],
  206. ['vie_integrative', 'vie intégrative','Voiron'],
  207. ['denis_ketels', 'Denis Ketels','Voiron'], #used only for changePassword test
  208. ['nico_faus_prod', 'Nico Faus Production','Grenoble'],
  209. ['hirundo_archi', 'Hirundo Architecture','Villard-de-Lans'],
  210. ['maison_bambous', 'Maison aux Bambous','Vinay'],
  211. ['recycleco', 'Recycleco','Saint-Marcellin'],
  212. ['elotine_photo', 'Elotine Photo','Grenoble'],
  213. ['boule_antan', 'La Boule d Antan','Villard-de-Lans'],
  214. ['la_remise', 'La Remise','Grenoble'],
  215. ['episol', 'Episol','Grenoble'],
  216. ['alter_mag', 'Alter Mag','Saint-Marcellin'],
  217. ['verre_a_soi', 'Le Verre à soi','Bilieu'],
  218. ['FluoDelik', 'Fluodélik','Méaudre'],
  219. ['1001_saveurs', '1001 Saveurs','Villard-de-Lans'],
  220. ['belle_verte', 'La Belle Verte','Susville'],
  221. ['kheops_boutique', 'Khéops boutique','Saint-Marcellin'],
  222. ['ferme_bressot', 'La ferme du Bressot','Beaulieu'],
  223. ['atelier_eltilo', 'Atelier Eltilo','Grenoble'],
  224. ['la_belle_verte', 'Belle Verte Permaculture','Sillans'],
  225. ['mon_vrac', 'Mon Vrac','Voiron'],
  226. ['le_marque_page', 'Le Marque Page','Saint-Marcellin'],
  227. ['boutik_creative', 'La Boutik Creative','Rives'],
  228. ['pain_beauvoir', 'Le Pain de Beauvoir','Beauvoir-en-Royans'],
  229. ['la_mandragore', 'La Mandragore','Grenoble'],
  230. ['jardins_epices', 'Les jardins epicés tout','Herbeys'],
  231. ['lib_colibri', 'Librairie Colibri','Voiron'],
  232. ['Claire_Dode', 'La Vie Claire Dode','Voiron'],
  233. ['fil_chantant', 'Le Fil qui Chante','Voiron'],
  234. ['epicerie_sol', 'Epicerie Solidaire Amandine','Voiron'],
  235. ['NaturaVie', 'Naturavie','Voiron'],
  236. ['montagne_arts', 'Les montagnarts','Valbonnais'],
  237. ['Biocoop', 'Biocoop','Chatte'],
  238. ['Alpes_EcoTour', 'Alpes Ecotourisme','Grenoble'],
  239. ['trankilou', 'Le Trankilou','Grenoble']
  240. ]
  241. groupes_locaux = [
  242. ['gl_grenoble', 'Groupe Local Grenoble','Grenoble'],
  243. ['gl_voiron', 'Groupe Local Voiron','Voiron'],
  244. ['gl_tullins', 'Groupe Local Tullins','Tullins'],
  245. ]
  246. for member in adherents_utilisateurs:
  247. create_user(
  248. group='Adhérents utilisateurs',
  249. name=member[1],
  250. login=member[0],
  251. email = member[0] + '@test.fr',
  252. password = '@@bbccdd',
  253. )
  254. create_user(
  255. group='Adhérents utilisateurs',
  256. name='Max Maz',
  257. login='mazmax',
  258. email = 'maxime.mazouth-laurol@cairn-monnaie.com',
  259. password = '@@bbccdd',
  260. )
  261. for member in adherents_prestataires:
  262. create_user(
  263. group='Adhérents prestataires',
  264. name=member[1],
  265. login=member[0],
  266. email = member[0] + '@test.fr',
  267. city = member[2],
  268. password = '@@bbccdd',
  269. )
  270. for member in groupes_locaux:
  271. create_user(
  272. group='Network administrators',
  273. name=member[1],
  274. login=member[0],
  275. email = member[0] + '@test.fr',
  276. city = member[2],
  277. password = '@@bbccdd',
  278. )
  279. porteurs = {
  280. 'P001': 'Porteur 1',
  281. 'P002': 'Porteur 2',
  282. 'P003': 'Porteur 3',
  283. 'P004': 'Porteur 4',
  284. }
  285. for login, name in porteurs.items():
  286. create_user(
  287. group='Porteurs',
  288. name=name,
  289. login=login,
  290. )
  291. # Récupération des constantes
  292. logger.info('Récupération des constantes depuis le YAML...')
  293. CYCLOS_CONSTANTS = None
  294. with open("/cyclos/cyclos_constants_"+ENV+".yml", 'r') as cyclos_stream:
  295. try:
  296. CYCLOS_CONSTANTS = yaml.load(cyclos_stream)
  297. except yaml.YAMLError as exc:
  298. assert False, exc
  299. # Impression billets mlc
  300. logger.info('Impression billets '+ LOCAL_CURRENCY_INTERNAL_NAME +'...')
  301. logger.debug(str(CYCLOS_CONSTANTS['payment_types']['impression_de_billets_mlc']) + "\r\n" +
  302. str(CYCLOS_CONSTANTS['currencies']['mlc']) + "\r\n" +
  303. str(CYCLOS_CONSTANTS['account_types']['compte_de_debit_mlc_billet']) + "\r\n" +
  304. str(CYCLOS_CONSTANTS['account_types']['stock_de_billets']))
  305. r = requests.post(network_web_services + 'payment/perform',
  306. headers={'Authorization': 'Basic {}'.format(base64.standard_b64encode(b'demo:demo').decode('utf-8'))}, # noqa
  307. json={
  308. 'type': CYCLOS_CONSTANTS['payment_types']['impression_de_billets_mlc'],
  309. 'amount': 126500,
  310. 'currency': CYCLOS_CONSTANTS['currencies']['mlc'],
  311. 'from': 'SYSTEM',
  312. 'to': 'SYSTEM',
  313. })
  314. logger.info('Impression billets ' + LOCAL_CURRENCY_INTERNAL_NAME + '... Terminé !')
  315. logger.debug(r.json())
  316. #@WARNING : specifique au cairn, à supprimer dans le rendu au mvt Sol
  317. today = datetime.now()
  318. def date_modify(nb_days):
  319. today = datetime.now()
  320. date = today + timedelta(days=nb_days)
  321. return date.strftime("%Y")+ '-' + date.strftime("%m")+'-'+date.strftime("%d")
  322. def credit_numeric_money_safe(amount):
  323. logger.info('Creation de MLC numeriques ...')
  324. r = requests.post(network_web_services + 'payment/perform',
  325. headers={'Authorization': 'Basic {}'.format(base64.standard_b64encode(b'admin_network:@@bbccdd').decode('utf-8'))},
  326. json={
  327. 'type': CYCLOS_CONSTANTS['payment_types']['creation_mlc_numeriques'],
  328. 'amount': amount,
  329. 'currency': CYCLOS_CONSTANTS['currencies']['mlc'],
  330. 'from': 'SYSTEM',
  331. 'to': 'SYSTEM',
  332. 'description': 'creation initiale de ' + LOCAL_CURRENCY_INTERNAL_NAME ,
  333. })
  334. logger.info('Creation de MLC numeriques... Terminé !')
  335. logger.debug(r.json())
  336. def credit_de_compte(member,amount):
  337. logger.info('Change numérique pour ' + member[1] + ' ...')
  338. r = requests.post(network_web_services + 'payment/perform',
  339. headers={'Authorization': 'Basic {}'.format(base64.standard_b64encode(b'admin_network:@@bbccdd').decode('utf-8'))},
  340. json={
  341. 'type': CYCLOS_CONSTANTS['payment_types']['credit_du_compte'],
  342. 'amount': amount,
  343. 'currency': CYCLOS_CONSTANTS['currencies']['mlc'],
  344. 'from': 'SYSTEM',
  345. 'to': member[0],
  346. 'description': 'dépôt ' + LOCAL_CURRENCY_INTERNAL_NAME ,
  347. 'customValues': [
  348. {
  349. 'field': str(CYCLOS_CONSTANTS['transaction_custom_fields']['bdc']),
  350. 'linkedEntityValue': id_bdc # ID de l'utilisateur Bureau de change
  351. },
  352. ]
  353. })
  354. logger.info('Change numérique pour ' + member[1] + '... Terminé !')
  355. logger.debug(r.json())
  356. def payment_inter_adherent(debitor, creditor, amount, offset):
  357. logger.info('Virement de ' + str(amount) + ' ' + LOCAL_CURRENCY_INTERNAL_NAME + ' ' + debitor + ' vers ' + creditor + ' ...')
  358. if offset == 0:
  359. cyclos_method = 'payment/perform'
  360. else:
  361. cyclos_method = 'scheduledPayment/perform'
  362. r = requests.post(network_web_services + cyclos_method,
  363. headers={'Authorization': 'Basic {}'.format(base64.standard_b64encode(b'admin_network:@@bbccdd').decode('utf-8'))},
  364. json={
  365. 'type': CYCLOS_CONSTANTS['payment_types']['virement_inter_adherent'],
  366. 'amount': amount,
  367. 'currency': CYCLOS_CONSTANTS['currencies']['mlc'],
  368. 'description': 'virement ' + LOCAL_CURRENCY_INTERNAL_NAME ,
  369. 'firstInstallmentDate': date_modify(offset),
  370. 'installmentsCount': 1,
  371. 'from': debitor,
  372. 'to': creditor,
  373. })
  374. logger.info('Virement de ' + str(amount) + ' ' + LOCAL_CURRENCY_INTERNAL_NAME + ' ' + debitor + ' vers ' + creditor + ' ... Terminé !')
  375. logger.debug(r.json())
  376. #Creation initiale de MLC numeriques
  377. credit_numeric_money_safe(10000000000)
  378. # Changes numériques afin d'avoir des comptes avec des soldes non nuls + des opérations à injecter dans l'appli CEL
  379. logger.info('Changes numériques en '+ LOCAL_CURRENCY_INTERNAL_NAME +' pour tous les pros à Grenoble...')
  380. logger.debug(str(CYCLOS_CONSTANTS['payment_types']['credit_du_compte']) + "\r\n" +
  381. str(CYCLOS_CONSTANTS['account_types']['compte_de_debit_mlc_numerique']) + "\r\n" +
  382. str(CYCLOS_CONSTANTS['account_types']['compte_d_adherent']))
  383. initial_credit = 1000
  384. for pro in adherents_prestataires:
  385. if pro[2] == 'Grenoble':
  386. credit_de_compte(pro, initial_credit)
  387. for person in adherents_utilisateurs:
  388. credit_de_compte(person, initial_credit)
  389. logger.info('Changes numériques en ' + LOCAL_CURRENCY_INTERNAL_NAME + '... Terminé !')
  390. logger.info('Virements immédiats de compte à compte en '+ LOCAL_CURRENCY_INTERNAL_NAME +' ...')
  391. #trankilou has null account again
  392. logger.info('"Le Trankilou" réalise un virement vers "la Remise" remettant son solde à 0')
  393. payment_inter_adherent('trankilou','la_remise',initial_credit,0)
  394. # for i in range(0,10):
  395. # payment_inter_adherent('DrDBrew','apogee_du_vin',10,0)
  396. logger.info('Virements immédiats de compte à compte en '+ LOCAL_CURRENCY_INTERNAL_NAME +'... Terminé !')
  397. logger.info('Virements programmés en '+ LOCAL_CURRENCY_INTERNAL_NAME +' de La Bonne Pioche vers AlterMag...')
  398. for i in range(0,5):
  399. payment_inter_adherent('labonnepioche','alter_mag',10,2)
  400. logger.info('Virements programmés en '+ LOCAL_CURRENCY_INTERNAL_NAME +' ... Terminé !!')
  401. logger.info('Fin du script !')