SQL Constraint Validation without incrementing the Sequence - Odoo 10 - odoo

I made a SQL Constraint in a form but we when enter something wrong it give us the constraint error but the Form is still not saved, but the problem is behind, the system make the incrementation of the sequence of this form. knowing that i make the sequence to get created only once the from get saved by super the create function.
How can make a validation of Constraint without incrementing the Sequence ?
This My Sequence Code
#api.model
def create(self, vals):
if vals.get('name','/')=='/':
sequence = self.env['ir.sequence'].next_by_code('archive.dossier')
vals['name'] = sequence
return super(oeArchiveDossier, self).create(vals)

There are two types (Implementation) of sequence used in odoo.
Standard
No Gap
Standard
If you selected this implementation type in sequence (it's default) then sequence will be created in postgresql and managed by postgresql. There is no such control over it. So in case any transactions gets failed then sequence (next number) won't rollbacked.
No Gap
If you select this implementation type then it will be managed by odoo, so in case any issue is there then no sequence number will be skipped.
To better control the validations there is an option in odoo to set method for constrains.
#api.constrains('field1', 'field2')
def check_validations(self):
## Perform Validation
## If condition not satisfied then raise an error.
if validation_conditions:
## Raise an error

Related

Setting the value of an Enum field in a temporary record

Total newbie in AL here; I'm experimenting with automated testing in Dynamics BC for a new deployment. As a very basic test, I'd like to simply create a new Item record in the Cronus test database and validate each field. I'm running into trouble when I try to select the value for an enum field. Here's the code I'm using:
Procedure AddGoodItem()
// [Given] Good item data
var
recItem: Record Item Temporary;
Begin
recItem."Description" := 'zzzz';
recItem.validate("Description");
recItem.Type := recItem.Type::Inventory;
recItem.Validate(Type);
recItem."Base Unit of Measure" := 'EA';
recItem.Validate("Base Unit of Measure");
recItem."Item Category Code" := 'FURNITURE';
recItem.validate("Item Category Code");
End;
When I run this in Cronus, I get the error:
You cannot change the Type field on Item because at least one Item Journal Line exists for this item.
If I comment the Type lines out, the process runs successfully.
Given that this is a temporary record, it shouldn't have any Item Journal Lines, should it? What am I missing?
The code in the OnValidate trigger still runs, even if you have marked the Record variable as temporary. In addition temporary is not inherited to the underlying variables meaning any Record variables used in the OnValidate trigger are not temporary (unless marked as such).
There are two options:
Leave out the line recItem.Validate(Type);, if the code run by the OnValidate trigger is not relevant in this case.
Replace the line recItem.Validate(Type); with your own clone of the code from the OnValidate trigger and then remove the unneeded parts.

Odoo force a field higher than x

In sales order, there is a field discount %. Is it possible to ensure the users only input value lower than x and it will not accept any value higher than x.
You can achieve this with creating a function using #api.onchange('discount') decorator on python code to ensure discount value is not higher than x, if higher then set x to discount field and also possible to show warning popup from that function.
But if python code change is not prefered, you can also achieve this with Automated action where you create a new rule on sale.order.line, set trigger On form modification, action Execute python code and add the following code
if record.discount > 10:
record.discount = 10
Where 10 is the value of x. This will ensure discount is always less than x.
Op1: If you want to ensure that behaviour you can add an sql constraint to the database, this will work fine if your X value never change, something like this:
_sql_constraints = [
('discount_less_than_X', 'CHECK(discount < X)', 'The discount value should be less than X'),
]
This constraint will trigger in all cases(create, write, import, SQL insert).
Replace X with the value desired.
Op2: Use decorator api.constrains, get X value from somewherelse and apply the restriccion, something like this:
#api.constrains('discount')
def _check_discount(self):
# Create a key:value parameter in `ir.config_parameter` table before.
disscount_max_value = self.env['ir.config_parameter'].sudo().get_param('disscount_max_value')
for rec in self:
if rec.disscount > int(disscount_max_value):
raise ValidationError('The discount value should be less than {}'.format(disscount_max_value))
I hope this answer can be helful for you.

Expected singleton odoo 9

After enter 2 and more new row in tree view and click on save get error
raise ValueError("Expected singleton: %s" % self)
ValueError: Expected singleton: my.model(2116, 2117)
My source code:
#api.depends('start', 'finish','stop')
def total_fun(self):
time1 = datetime.strptime(self.start, "%Y-%m-%d %H:%M:%S")
time2 = datetime.strptime(self.finish, "%Y-%m-%d %H:%M:%S")
self.total = round(((time2 - time1).seconds / float(60*60) - self.stop))
Error message says -> expected singleton this means: you are using recordset instead of record.
To fix this use
for rec in self:
in the begining of function, and then use rec instead of self
As you can see in the error message Expected singleton: my.model(2116, 2117)
By default in odoo the self is always a recordSet (means it can contain more then one record.) so when you do self.getSomeField here odoo will be confused wich record you want to get the value from.
if you don't tell odoo that make sure that the self will always contain one record when you acces an attribute if the recordSet contains more than one record this error is raised.
Now how to tell odoo make sure there is always one record is by adding #api.one decorator to the method. but is not recommended because odoo in your case there is two record so he will loop and call the method for each record and pass a recordSet that have only that record. imagine that you execute a search or any communication with database.
so don't use #api.one only if you are sure of what you are doing because you can make 10000 method call and interact with database.
like this example using #api.one:
# every call to this method you will execute a search.
self.env['some.model'].search([('m2o_id' , '=', self.id)]
you can do this before the loop:
# one query for all record with one call to the method
result = self.env['some.model'].search([('m2o_id' , 'in', self.ids)]
for rec in self:
# use result here
# or here ..

OpenERP - how to get form field value in python code

I have a field in the XML file, categ_id. I need to access the value of that field in my Python code, in product_template class. I tried vals as a paremeter but it did not work.
If you can give me an example object.field_name as it relates to the case I have described.
Nebojsa - your question is not understandable at all, but I'll try to answer it. You can get the value of categ_id in two or even three ways:
vals.get('categ_id') - this is the way to go when you are creating a new record or updating existing one with change in categ_id field - otherwise you'll get an error or NoneType defined.
template = self.pool.get('product.template).browse(cr, uid, ids) and then template.categ_id.id - to get the value when you do have an id of the record, so you can ask database of value stored or in transaction, if there were any changes.
third opition is the dirtiest one, because it is just cr.execute("SELECT categ_id FROM product_template WHERE id = %s", (ids[0],)) and then category_id = cr.fetchall() - it is not always good option to use that, as it asks for records already existing in database (not counting these in transaction)

Getting DatabaseError: multiple default values specified for column

I can't figure out what to do to avoid this error whenever I try to make a migration using South in one of my Django projects:
ERROR:
Running migrations for askbot:
Migrating forwards to 0006_auto__del_field_tagplus_tag_ptr__add_field_tagplus_id__add_field_tagpl.
askbot:0006_auto__del_field_tagplus_tag_ptr__add_field_tagplus_id__add_field_tagpl
FATAL ERROR - The following SQL query failed: ALTER TABLE "tagplus" ADD COLUMN "id" serial NOT >NULL PRIMARY KEY DEFAULT -1;
The error was: multiple default values specified for column "id" of table "tagplus"
Error in migration: >askbot:0006_auto__del_field_tagplus_tag_ptr__add_field_tagplus_id__add_field_tagpl
DatabaseError: multiple default values specified for column "id" of table "tagplus"
MIGRATION FILE 0006 CODE (Partial):
class Migration(SchemaMigration):
def forwards(self, orm):
# Deleting field 'TagPlus.tag_ptr'
db.delete_column(u'tagplus', u'tag_ptr_id')
# Adding field 'TagPlus.id'
db.add_column(u'tagplus', u'id',
self.gf('django.db.models.fields.AutoField')(default=0, primary_key=True),
keep_default=False)
# Adding field 'TagPlus.name'
db.add_column(u'tagplus', 'name',
self.gf('django.db.models.fields.CharField')(default=0, unique=True, max_length=255),
keep_default=False)
Thanks!
EDIT:
I Guess the error has something to do with this choice I was prompted while creating the migration file.
? The field 'TagPlus.tag_ptr' does not have a default specified, yet is NOT NULL.
? Since you are removing this field, you MUST specify a default
? value to use for existing rows. Would you like to:
? 1. Quit now.
? 2. Specify a one-off value to use for existing columns now
? 3. Disable the backwards migration by raising an exception; you can edit the migration to fix it later
? Please select a choice:
I selected 'specify one-off value' and I set this value to 0
You are anyways saying keep_default=False. So remove that default=0 from your code
db.add_column(u'tagplus', u'id',
self.gf('django.db.models.fields.AutoField')(primary_key=True),
keep_default=False)
Per SQL it should be (remove the NOT NULL)
ALTER TABLE tagplus ADD COLUMN id serial PRIMARY KEY
See this document which explains the reason behind this error http://www.postgresql.org/docs/8.3/interactive/datatype-numeric.html#DATATYPE-SERIAL
There are two things to note:
Django will use 'id' as the default primary key if you haven't manually set one before, see here.
Postgres does not really have a 'serial' type. To resolve this issue, try to replace:
# Adding field 'TagPlus.id'
db.add_column(u'tagplus', u'id', self.gf('django.db.models.fields.AutoField')(default=0, primary_key=True), keep_default=False)
with:
# Adding field 'TagPlus.id'
if 'postgres' in db.backend_name.lower():
db.execute("CREATE SEQUENCE tagplus_id_seq")
db.execute("SELECT setval('tagplus_id_seq', (SELECT MAX(id) FROM tagplus))")
db.execute("ALTER TABLE tagplus ADD COLUMN id SET DEFAULT nextval('tagplus_id_seq'::regclass)")
db.execute("ALTER SEQUENCE tagplus_id_seq OWNED BY tagplus.id")
else:
db.add_column(u'tagplus', u'id', self.gf('django.db.models.fields.AutoField')(default=0, primary_key=True), keep_default=False)