def update(self, inactivity_period): """ Updates the last_poll and last_presence of the current user :param inactivity_period: duration in milliseconds """ presence = self.search([('user_id', '=', self._uid)], limit=1) # compute last_presence timestamp last_presence = datetime.datetime.now() - datetime.timedelta( milliseconds=inactivity_period) values = { 'last_poll': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT), } # update the presence or a create a new one if not presence: # create a new presence for the user values['user_id'] = self._uid values['last_presence'] = last_presence.strftime( DEFAULT_SERVER_DATETIME_FORMAT) self.create(values) else: # update the last_presence if necessary, and write values if datetime.datetime.strptime( presence.last_presence, DEFAULT_SERVER_DATETIME_FORMAT) < last_presence: values['last_presence'] = last_presence.strftime( DEFAULT_SERVER_DATETIME_FORMAT) # Hide transaction serialization errors, which can be ignored, the presence update is not essential with tools.mute_logger('noblecrm.sql_db'): presence.write(values) # avoid TransactionRollbackError self.env.cr.commit() # TODO : check if still necessary
def _procure_calculation_orderpoint(self): with api.Environment.manage(): # As this function is in a new thread, I need to open a new cursor, because the old one may be closed new_cr = self.pool.cursor() self = self.with_env(self.env(cr=new_cr)) scheduler_cron = self.sudo().env.ref( 'stock.ir_cron_scheduler_action') # Avoid to run the scheduler multiple times in the same time try: with tools.mute_logger('noblecrm.sql_db'): self._cr.execute( "SELECT id FROM ir_cron WHERE id = %s FOR UPDATE NOWAIT", (scheduler_cron.id, )) except Exception: _logger.info( 'Attempt to run procurement scheduler aborted, as already running' ) self._cr.rollback() self._cr.close() return {} for company in self.env.user.company_ids: self.env['procurement.group'].run_scheduler( use_new_cursor=self._cr.dbname, company_id=company.id) new_cr.close() return {}
def create_variant_ids(self): Product = self.env["product.product"] AttributeValues = self.env['product.attribute.value'] for tmpl_id in self.with_context(active_test=False): # adding an attribute with only one value should not recreate product # write this attribute on every product to make sure we don't lose them variant_alone = tmpl_id.attribute_line_ids.filtered(lambda line: line.attribute_id.create_variant and len(line.value_ids) == 1).mapped('value_ids') for value_id in variant_alone: updated_products = tmpl_id.product_variant_ids.filtered(lambda product: value_id.attribute_id not in product.mapped('attribute_value_ids.attribute_id')) updated_products.write({'attribute_value_ids': [(4, value_id.id)]}) # iterator of n-uple of product.attribute.value *ids* variant_matrix = [ AttributeValues.browse(value_ids) for value_ids in itertools.product(*(line.value_ids.ids for line in tmpl_id.attribute_line_ids if line.value_ids[:1].attribute_id.create_variant)) ] # get the value (id) sets of existing variants existing_variants = {frozenset(variant.attribute_value_ids.filtered(lambda r: r.attribute_id.create_variant).ids) for variant in tmpl_id.product_variant_ids} # -> for each value set, create a recordset of values to create a # variant for if the value set isn't already a variant to_create_variants = [ value_ids for value_ids in variant_matrix if set(value_ids.ids) not in existing_variants ] # check product variants_to_activate = self.env['product.product'] variants_to_unlink = self.env['product.product'] for product_id in tmpl_id.product_variant_ids: if not product_id.active and product_id.attribute_value_ids.filtered(lambda r: r.attribute_id.create_variant) in variant_matrix: variants_to_activate |= product_id elif product_id.attribute_value_ids.filtered(lambda r: r.attribute_id.create_variant) not in variant_matrix: variants_to_unlink |= product_id if variants_to_activate: variants_to_activate.write({'active': True}) # create new product for variant_ids in to_create_variants: new_variant = Product.create({ 'product_tmpl_id': tmpl_id.id, 'attribute_value_ids': [(6, 0, variant_ids.ids)] }) # unlink or inactive product for variant in variants_to_unlink: try: with self._cr.savepoint(), tools.mute_logger('noblecrm.sql_db'): variant.unlink() # We catch all kind of exception to be sure that the operation doesn't fail. except (psycopg2.Error, except_orm): variant.write({'active': False}) pass return True
def test_30_stripe_form_management(self): self.assertEqual(self.stripe.environment, 'test', 'test without test environment') # typical data posted by Stripe after client has successfully paid stripe_post_data = { u'amount': 4700, u'amount_refunded': 0, u'application_fee': None, u'balance_transaction': u'txn_172xfnGMfVJxozLwssrsQZyT', u'captured': True, u'created': 1446529775, u'currency': u'eur', u'customer': None, u'description': None, u'destination': None, u'dispute': None, u'failure_code': None, u'failure_message': None, u'fraud_details': {}, u'id': u'ch_172xfnGMfVJxozLwEjSfpfxD', u'invoice': None, u'livemode': False, u'metadata': { u'reference': u'SO100' }, u'object': u'charge', u'paid': True, u'receipt_email': None, u'receipt_number': None, u'refunded': False, u'refunds': { u'data': [], u'has_more': False, u'object': u'list', u'total_count': 0, u'url': u'/v1/charges/ch_172xfnGMfVJxozLwEjSfpfxD/refunds' }, u'shipping': None, u'source': { u'address_city': None, u'address_country': None, u'address_line1': None, u'address_line1_check': None, u'address_line2': None, u'address_state': None, u'address_zip': None, u'address_zip_check': None, u'brand': u'Visa', u'country': u'US', u'customer': None, u'cvc_check': u'pass', u'dynamic_last4': None, u'exp_month': 2, u'exp_year': 2022, u'fingerprint': u'9tJA9bUEuvEb3Ell', u'funding': u'credit', u'id': u'card_172xfjGMfVJxozLw1QO6gYNM', u'last4': u'4242', u'metadata': {}, u'name': u'*****@*****.**', u'object': u'card', u'tokenization_method': None }, u'statement_descriptor': None, u'status': u'succeeded' } tx = self.env['payment.transaction'].create({ 'amount': 4700, 'acquirer_id': self.stripe.id, 'currency_id': self.currency_euro.id, 'reference': 'SO100', 'partner_name': 'Norbert Buyer', 'partner_country_id': self.country_france.id }) # validate it tx.form_feedback(stripe_post_data, 'stripe') self.assertEqual(tx.state, 'done', 'Stripe: validation did not put tx into done state') self.assertEqual(tx.acquirer_reference, stripe_post_data.get('id'), 'Stripe: validation did not update tx id') # reset tx tx.write({ 'state': 'draft', 'date_validate': False, 'acquirer_reference': False }) # simulate an error stripe_post_data['status'] = 'error' stripe_post_data.update({ u'error': { u'message': u"Your card's expiration year is invalid.", u'code': u'invalid_expiry_year', u'type': u'card_error', u'param': u'exp_year' } }) with mute_logger('noblecrm.addons.payment_stripe.models.payment'): tx.form_feedback(stripe_post_data, 'stripe') # check state self.assertEqual( tx.state, 'error', 'Stipe: erroneous validation did not put tx into error state')
def test_create_from_ui(self): """ Simulation of sales coming from the interface, even after closing the session """ FROMPRODUCT = object() def compute_tax(product, price, taxes=FROMPRODUCT, qty=1): if taxes is FROMPRODUCT: taxes = product.taxes_id currency = self.pos_config.pricelist_id.currency_id taxes = taxes.compute_all(price, currency, qty, product=product)['taxes'] untax = price * qty return untax, sum(tax.get('amount', 0.0) for tax in taxes) # I click on create a new session button self.pos_config.open_session_cb() current_session = self.pos_config.current_session_id num_starting_orders = len(current_session.order_ids) untax, atax = compute_tax(self.carotte, 0.9) carrot_order = { 'data': { 'amount_paid': untax + atax, 'amount_return': 0, 'amount_tax': atax, 'amount_total': untax + atax, 'creation_date': fields.Datetime.now(), 'fiscal_position_id': False, 'pricelist_id': self.pos_config.available_pricelist_ids[0].id, 'lines': [[ 0, 0, { 'discount': 0, 'id': 42, 'pack_lot_ids': [], 'price_unit': 0.9, 'product_id': self.carotte.id, 'qty': 1, 'tax_ids': [(6, 0, self.carotte.taxes_id.ids)] } ]], 'name': 'Order 00042-003-0014', 'partner_id': False, 'pos_session_id': current_session.id, 'sequence_number': 2, 'statement_ids': [[ 0, 0, { 'account_id': self.env.user.partner_id. property_account_receivable_id.id, 'amount': untax + atax, 'journal_id': self.pos_config.journal_ids[0].id, 'name': fields.Datetime.now(), 'statement_id': current_session.statement_ids[0].id } ]], 'uid': '00042-003-0014', 'user_id': self.env.uid }, 'id': '00042-003-0014', 'to_invoice': False } untax, atax = compute_tax(self.courgette, 1.2) zucchini_order = { 'data': { 'amount_paid': untax + atax, 'amount_return': 0, 'amount_tax': atax, 'amount_total': untax + atax, 'creation_date': fields.Datetime.now(), 'fiscal_position_id': False, 'pricelist_id': self.pos_config.available_pricelist_ids[0].id, 'lines': [[ 0, 0, { 'discount': 0, 'id': 3, 'pack_lot_ids': [], 'price_unit': 1.2, 'product_id': self.courgette.id, 'qty': 1, 'tax_ids': [(6, 0, self.courgette.taxes_id.ids)] } ]], 'name': 'Order 00043-003-0014', 'partner_id': False, 'pos_session_id': current_session.id, 'sequence_number': self.pos_config.journal_id.id, 'statement_ids': [[ 0, 0, { 'account_id': self.env.user.partner_id. property_account_receivable_id.id, 'amount': untax + atax, 'journal_id': self.pos_config.journal_ids[0].id, 'name': fields.Datetime.now(), 'statement_id': current_session.statement_ids[0].id } ]], 'uid': '00043-003-0014', 'user_id': self.env.uid }, 'id': '00043-003-0014', 'to_invoice': False } untax, atax = compute_tax(self.onions, 1.28) onions_order = { 'data': { 'amount_paid': untax + atax, 'amount_return': 0, 'amount_tax': atax, 'amount_total': untax + atax, 'creation_date': fields.Datetime.now(), 'fiscal_position_id': False, 'pricelist_id': self.pos_config.available_pricelist_ids[0].id, 'lines': [[ 0, 0, { 'discount': 0, 'id': 3, 'pack_lot_ids': [], 'price_unit': 1.28, 'product_id': self.onions.id, 'qty': 1, 'tax_ids': [[6, False, self.onions.taxes_id.ids]] } ]], 'name': 'Order 00044-003-0014', 'partner_id': False, 'pos_session_id': current_session.id, 'sequence_number': self.pos_config.journal_id.id, 'statement_ids': [[ 0, 0, { 'account_id': self.env.user.partner_id. property_account_receivable_id.id, 'amount': untax + atax, 'journal_id': self.pos_config.journal_ids[0].id, 'name': fields.Datetime.now(), 'statement_id': current_session.statement_ids[0].id } ]], 'uid': '00044-003-0014', 'user_id': self.env.uid }, 'id': '00044-003-0014', 'to_invoice': False } # I create an order on an open session self.PosOrder.create_from_ui([carrot_order]) self.assertEqual(num_starting_orders + 1, len(current_session.order_ids), "Submitted order not encoded") # I resubmit the same order self.PosOrder.create_from_ui([carrot_order]) self.assertEqual(num_starting_orders + 1, len(current_session.order_ids), "Resubmitted order was not skipped") # I close the session current_session.action_pos_session_closing_control() self.assertEqual(current_session.state, 'closed', "Session was not properly closed") self.assertFalse(self.pos_config.current_session_id, "Current session not properly recomputed") # I keep selling after the session is closed with mute_logger('noblecrm.addons.point_of_sale.models.pos_order'): self.PosOrder.create_from_ui([zucchini_order, onions_order]) rescue_session = self.PosSession.search([('config_id', '=', self.pos_config.id), ('state', '=', 'opened'), ('rescue', '=', True)]) self.assertEqual( len(rescue_session), 1, "One (and only one) rescue session should be created for orphan orders" ) self.assertIn("(RESCUE FOR %s)" % current_session.name, rescue_session.name, "Rescue session is not linked to the previous one") self.assertEqual(len(rescue_session.order_ids), 2, "Rescue session does not contain both orders") # I close the rescue session rescue_session.action_pos_session_closing_control() self.assertEqual(rescue_session.state, 'closed', "Rescue session was not properly closed")