Integrating custom module with odoo website - odoo

I have a custom module called admission form with some fields suppose name, phone, email, etc. how to add this form to website module using templatr to work like contact form in contact us page when filled data is automatically created in new leads. instead of leads i want it to transfer the information to my custom module.
Summary: instruction to relate website to custom module.
class AdmissionForm(models.Model):
_name = 'admission.form'
name = fields.Char()
phone = fields.Integer()
email = fields.Char()
faculty = field.Many2one('res.faculty')

In ODOO Whenever you want to performe some task at the time of creation ,then you must override create method in your model (:admission.form).
Let say you want to create a partner just after creation of the record in admission.form model then follow these steps:
Override create method .
Call the super with the argument and hold it value in result.
Now do your task .
return result.
Code snippet:
#api.model
def create(self, vals):
result = super(AdmissionForm, self).create(vals)
new_vals = dict(name=result.name,
phone=result.phone,
email=result.email,
is_company=1,
supplier=1,
customer=1,
)
self.env['res.partner'].create(new_vals)
return result
In case if you want to do some task before creation of record then follow these steps:
Override create method .
Do your task .
Call the super with the argument and return it.
#api.model
def create(self, vals):
new_vals = dict(name=vals.get('name'),
phone=vals.get('phone'),
email=vals.get('email'),
is_company=1,
supplier=1,
customer=1,
)
partner=self.env['res.partner'].create(new_vals)
return super(AdmissionForm, self).create(vals)

Related

Python Telegram Bot ConversationHandler not working with webhook

I want to make a ConversationHandler in my bot that is using a webhook, the ConversationHandler only runs the function at the entry point, after that neither does it run the state function, nor does it run the fallback function. This CommandHandler runs fine when bot is run by polling.
ConversationHandler:
conv_handler = ConversationHandler(
entry_points=[CommandHandler("start", start)],
states={
NEW_PROJECT: [CallbackQueryHandler(project_name)],
PROJECT_NAME: [MessageHandler(Filters.regex(".*"), store_name_maybe_project_type)],
PROJECT_TYPE: [CallbackQueryHandler(store_type_maybe_admin)]
},
fallbacks=[CommandHandler('cancel', cancel)],
)
All the required functions:
def start(update, context):
# Gives button of add project
# We can use for loop to display buttons
keyboard = [
[InlineKeyboardButton("Add Project", callback_data="add_project")],
]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text("You have no projects right now.", reply_markup=reply_markup)
# if existing project then PROJECT or else NEW_PROJECT
return NEW_PROJECT
def project_name(update, context):
# asks for project name
query = update.callback_query
update.message.reply_text(text="Okay, Please enter your project name:")
return PROJECT_NAME
def store_name_maybe_project_type(update, context):
# stores project name and conditionally asks for project type
print(update.message.text)
keyboard = [
[InlineKeyboardButton("Telegram Group", callback_data="group")],
[InlineKeyboardButton("Telegram Channel", callback_data="channel")]
]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text("What do you want to make?", reply_markup=reply_markup)
return PROJECT_TYPE
def store_type_maybe_admin(update, context):
# stores project type and conditonally asks for making admin
print(update.message.text)
keyboard = [[InlineKeyboardButton("Done", callback_data="done")]]
reply_markup = InlineKeyboardMarkup(keyboard)
update.message.reply_text(f"Make a private {update.message.text} and make this bot the admin", reply_markup=reply_markup)
return ConversationHandler.END
def cancel(update, context):
update.message.reply_text("Awww, that's too bad")
return ConversationHandler.END
This is how I set up the webhook(I think the problem is here somewhere):
#app.route(f"/{TOKEN}", methods=["POST"])
def respond():
"""Run the bot."""
update = telegram.Update.de_json(request.get_json(force=True), bot)
dispatcher = setup(bot, update)
dispatcher.process_update(update)
return "ok"
The setup function
def setup(bot, update):
# Create bot, update queue and dispatcher instances
dispatcher = Dispatcher(bot, None, workers=0)
##### Register handlers here #####
bot_handlers = initialize_bot(update)
for handler in bot_handlers:
dispatcher.add_handler(handler)
return dispatcher
And then I manually setup the webhook by using this route:
#app.route("/setwebhook", methods=["GET", "POST"])
def set_webhook():
s = bot.setWebhook(f"{URL}{TOKEN}")
if s:
return "webhook setup ok"
else:
return "webhook setup failed"
The add project button doesn't do anything.
ConversationHandler stores the current state in memory, so it's lost once the conv_handler reaches the end of it's lifetime (i.e. the variable is deleted or the process is shut down). Now your snippets don't show where you initialize the ConversationHandler, but I have the feeling that you create it anew for every incoming update - and every new instance doesn't have the knowledge of the previous one.
I have that feeling, because you create a new Dispatcher for every update as well. That's not necessary and in fact I'd strongly advise against it. Not only does it take time to initialize the Dispatcher, which you could save, but also if you're using chat/user/bot_data, the data get's lost every time you create a new instance.
The initialize_bot function is called in setup, where you create the new Dispatcher, which is why my guess would be that you create a new ConversationHandler for every update. Also it seems odd to me that the return value of that function seems to be dependent on the update - the handlers used by your dispatcher should be fixed ...
Disclaimer: I'm currently the maintainer of python-telegram-bot

Should objects know of the objects they're used in?

class Item:
def __init__(self, box, description):
self._box = box
self._description = description
class Box:
def __init__(self):
self.item_1 = Item(self, 'A picture')
self.item_2 = Item(self, 'A pencil')
#etc
old_stuff = Box()
print(old_stuff.item_1.box.item_1.box.item_2.box.item_1)
Above is shown an example piece of code which demonstrates my problem better than I ever could with plain text. Is there a better way to find in what box something is? (In what box is the picture?) Since I am not particularly fond of the above solution because it allows for this weird up and down calling which could go on forever. Is there a better way to solve this problem or is this just a case of: If it's stupid and it works, it ain't stupid.
Note: this trick isn't python specific. It's doable in all object-oriented programming laguages.
There is no right or wrong way to do this. The solution depends on how you want to use the object.
If your use-case requires that an item know in which box it is stored, then you need a reference to the box; if not, then you don't need the association.
Similarly, if you need to which items are in a given box, then you need references to the items in the box object.
The immediate requirement (that is, the current context) always dictates how one designs a class model; for example, one models an item or a box differently in a UI layer from how one would model it in a service layer.
You must introduce new class - ItemManager or simply dict or other external structure to store information about which box contain your item:
class Item:
def __init__(self, description):
self.description = description
class Box:
def __init__(self, item_1, item_2):
self.item_1 = item_1
self.item_2 = item_2
class ItemManager:
def __init__(self):
self.item_boxes = {}
def register_item(self, item, box):
self.item_boxes[item] = box
def deregister_item(self, item):
del self.item_boxes[item]
def get_box(self, item):
return self.item_boxes.get(item, None)
item_manager = ItemManager()
item_1 = Item("A picture")
item_2 = Item("A pencil")
item_3 = Item("A teapot")
old_stuff = Box(item_1, item_2)
item_manager.register_item(item_1, old_stuff)
item_manager.register_item(item_2, old_stuff)
new_stuff = Box(item_3, None)
item_manager.register_item(item_3, new_stuff)
box_with_picture = item_manager.get_box(item_2)
print box_with_picture.item_1.description
Also see SRP: an item should not know which box contains it.

POST a list to the API, update or create depending on the existence of that instance

I have a view which allows me to post multiple entries to a model. Currently if I add all new entries, they are added successfully. But if I add any entry for which the pk already exists, it naturally throws a serializer error and nothing gets updated.
I wish to write a method which will let me post multiple entries, but automatically either update an existing one OR add a new one successfully depending the existence of that instance.
The idea of a customized ListSerialzer is the closest thing I came across to achieve this but still not clear if I can do this.
Has anyone ever implemented anything like this ?
In views.py:
def post(self,request,format=None):
data = JSONParser().parse(request)
serializer = PanelSerializer(data=data,many=True)
if serializer.is_valid():
serializer.save()
return JsonResponse({"success":"true","content":serializer.data}, status=201)
return JsonResponse({'success': "false",'errorCode':"1",'errorMessage':serializer.errors}, status=400)
in serializers.py:
class PanelSerializer(serializers.ModelSerializer):
class Meta:
model = Panel
fields = ('pId','pGuid','pName', 'pVoltage', 'pAmperage','pPermission', 'pPresent', 'pSelf','pInfo')
def create(self, validated_data):
logger.info('Information incoming_1!')
print ("Atom")
return Panel.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.pId = validated_data.get('pId', instance.pId)
instance.pGuid = validated_data.get('pId', instance.pGuid)
instance.pName = validated_data.get('pName', instance.pName)
instance.pVoltage = validated_data.get('pVoltage', instance.pVoltage)
instance.pAmperage = validated_data.get('pAmperage', instance.pAmperage)
instance.pPermission = validated_data.get('pPermission', instance.pPermission)
instance.pPresent = validated_data.get('pPresent', instance.pPresent)
instance.pSelf = validated_data.get('pSelf', instance.pSelf)
instance.pInfo = validated_data.get('pInfo', instance.pInfo)
instance.save()
return instance
This is how the code stands as of now. I believe I will mainly need to either work on the update method of my serializer or first change it to a ListSerializer and write some custom logic again in the update method.

Is there any way to call a function when a record is requested to be shown in a form view?

I would like to update every record when it is shown in form view. Is there anyway to define some sort of a callback to update the record BEFORE it is shown?
One method that I could think of is by adding a dummy field to form view with an on_change attribute. But it feels kinda hacky.
You can use function field. Function is called when the form loading and at the time of new record creation before and after.
When you create a new record this function field will take value as a William and after saving value will change and become odedra
Here is example of char return type of function :
def _default_get(self, cr, uid, context=None):
print " This function called before new record create "
res = 'William'
return res
def _set_value(self, cr, uid, ids, name, args, context=None):
print " This function called at time of saving record and form view load "
res = {}
for i in self.browse(cr, uid, ids, context=context):
res[i.id] = 'odedra'
return res
_columns = {
'value': fields.function(_set_value, type='char', string='Value'),
}
_defaults = {
'value': _default_get,
}
NOTE:
As per your requirement, you may change function field return type.
Using a functional field seems odd but then making a read not idempotent is also unusual.
I would set a context in the window action of your form so you can test for it and limit the effect of this and then override the default_get method for new records and the read method for existing records on your model.

django Autocomplete-light how to choose a specific method from a mode

I am new at django and autocomplete-light. I try to get a different fields of the model from autocomplete-light, but it always return the same field. And the reason is because def in the Model defined one field. So I created another def, but can not make autocomplete-light to call that specific def. Here is my code.
models.py:
class Item(models.Model):
...
serial_number=models.CharField(max_length=100, unique=True)
barcode=models.CharField(max_length=25, unique=True)
def __unicode__(self):
return self.serial_number
def bar(self):
return self.barcode
.......
autocomplete_light_registry.py
autocomplete_light.register(Item,
name='AutocompleteItemserial',
search_fields=['serial_number'],
)
autocomplete_light.register(Item,
name='AutocompleteItembarcode',
search_fields=['barcode'],
)
Here is the issue: when I try to get the barcodes from the autocomplete-light, it returns serial_numbers. No matter what I try to get from the Item model, it always returns the serial number. I really appreciate for the answers. Thank you.
Just in case, here is the form.py
forms.py
class ItemForm(forms.ModelForm):
widgets = {
'serial_number': autocomplete_light.TextWidget('AutocompleteItemserial'),
'barcode': autocomplete_light.TextWidget('AutocompleteItembarcode'),
}
Although this is an old post but as I just faced the same issue therefore I am sharing my solution.
The reason autocomplete is returning serial_number is because django-autocomplete-light uses the __unicode__ method of the model to show the results. In your AutocompleteItembarcode all that is being done is autocomplete-light is searching by barcode field of Item.
Try the following.
In app/autocomplete_light_registry.py
from django.utils.encoding import force_text
class ItemAutocomplete(autocomplete_light.AutocompleteModelBase):
search_fields = ['serial_number']
model = Item
choices = Item.objects.all()
def choice_label(self, choice):
"""
Return the human-readable representation of a choice.
"""
barcode = Item.objects.get(pk=self.choice_value(choice)).barcode
return force_text(barcode)
autocomplete_light.register(ItemAutocomplete)
For more help you can have a look at the source code.