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('actpy.sql_db'): presence.write(values) # avoid TransactionRollbackError self.env.cr.commit() # TODO : check if still necessary
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('actpy.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 update_records(model, src, field_model='model', field_id='res_id'): Model = self.env[model] if model in self.env else None if Model is None: return records = Model.sudo().search([(field_model, '=', 'res.partner'), (field_id, '=', src.id)]) try: with mute_logger('actpy.sql_db'), self._cr.savepoint(): return records.sudo().write({field_id: dst_partner.id}) except psycopg2.Error: # updating fails, most likely due to a violated unique constraint # keeping record with nonexistent partner_id is useless, better delete it return records.sudo().unlink()
def test_rename_unique(self): """ one cannot create two fields with the same name on a given model """ field1 = self.create_field('x_foo') field2 = self.create_field('x_bar') with self.assertRaises(IntegrityError), mute_logger('actpy.sql_db'): field2.name = field1.name
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('actpy.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")
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('actpy.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 _update_foreign_keys(self, src_partners, dst_partner): """ Update all foreign key from the src_partner to dst_partner. All many2one fields will be updated. :param src_partners : merge source res.partner recordset (does not include destination one) :param dst_partner : record of destination res.partner """ _logger.debug( '_update_foreign_keys for dst_partner: %s for src_partners: %s', dst_partner.id, str(src_partners.ids)) # find the many2one relation to a partner Partner = self.env['res.partner'] relations = self._get_fk_on('res_partner') for table, column in relations: if 'base_partner_merge_' in table: # ignore two tables continue # get list of columns of current table (exept the current fk column) query = "SELECT column_name FROM information_schema.columns WHERE table_name LIKE '%s'" % ( table) self._cr.execute(query, ()) columns = [] for data in self._cr.fetchall(): if data[0] != column: columns.append(data[0]) # do the update for the current table/column in SQL query_dic = { 'table': table, 'column': column, 'value': columns[0], } if len(columns) <= 1: # unique key treated query = """ UPDATE "%(table)s" as ___tu SET %(column)s = %%s WHERE %(column)s = %%s AND NOT EXISTS ( SELECT 1 FROM "%(table)s" as ___tw WHERE %(column)s = %%s AND ___tu.%(value)s = ___tw.%(value)s )""" % query_dic for partner in src_partners: self._cr.execute( query, (dst_partner.id, partner.id, dst_partner.id)) else: try: with mute_logger('actpy.sql_db'), self._cr.savepoint(): query = 'UPDATE "%(table)s" SET %(column)s = %%s WHERE %(column)s IN %%s' % query_dic self._cr.execute(query, ( dst_partner.id, tuple(src_partners.ids), )) # handle the recursivity with parent relation if column == Partner._parent_name and table == 'res_partner': query = """ WITH RECURSIVE cycle(id, parent_id) AS ( SELECT id, parent_id FROM res_partner UNION SELECT cycle.id, res_partner.parent_id FROM res_partner, cycle WHERE res_partner.id = cycle.parent_id AND cycle.id != cycle.parent_id ) SELECT id FROM cycle WHERE id = parent_id AND id = %s """ self._cr.execute(query, (dst_partner.id, )) # NOTE JEM : shouldn't we fetch the data ? except psycopg2.Error: # updating fails, most likely due to a violated unique constraint # keeping record with nonexistent partner_id is useless, better delete it query = 'DELETE FROM "%(table)s" WHERE "%(column)s" IN %%s' % query_dic self._cr.execute(query, (tuple(src_partners.ids), ))