Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Loje technical training #63

Closed
wants to merge 35 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
f743823
hello world ! in the readme test
jlong1307 Apr 16, 2024
1a48e71
[ADD] LOJE Addition of the "estate" Model, creation of the "estate" m…
jlong1307 Apr 17, 2024
7bbd575
[TAG] LOJE: Add the "security" folder with the CSV file inside contai…
jlong1307 Apr 17, 2024
8c252f4
ADD: add views using estate_property_views and estate_menus.
jlong1307 Apr 17, 2024
ea5c392
[IMP] LOJE: Add fields to the models: estate_property
jlong1307 Apr 17, 2024
33cdbe1
[IMP] LOJE, standardize my code in the estate_property file.
jlong1307 Apr 17, 2024
cfaa45d
[IMP] Loje: Setting up a tree view with the fields; name, postcode, b…
jlong1307 Apr 17, 2024
c6e3259
"[IMP] LOJE, addition of my form for estate property and addition of …
jlong1307 Apr 18, 2024
201c9e0
"[ADD] Chapter 7: Module Relationships, Adding Estate Property Type, …
jlong1307 Apr 18, 2024
968618c
"[IMP] Chapter 8: Computed Fields and Onchanges; First exercise addin…
jlong1307 Apr 18, 2024
2661e7a
[IMP] Chapter 8: Add the dependencies and inverse function
jlong1307 Apr 19, 2024
0d64208
[IMP] chapter 8: Add the onchange_garden in the estate_pro file
jlong1307 Apr 19, 2024
9730bad
[IMP] Chapter 9: Add the Cancel and sold buttom into the estate prope…
jlong1307 Apr 19, 2024
8893455
[IMP] chapter 10: Add the constraints to their corresponding models :…
jlong1307 Apr 19, 2024
3d9e752
[IMP] Chapter 10: Add python constrains : Add a constraint so that th…
jlong1307 Apr 19, 2024
bf9a0c7
[IMP] Chapter 11: Add an inline list view, add theOne 2many fild prop…
jlong1307 Apr 19, 2024
6cfe349
[IMP] Chapter 11: FInish the chapter 11 : Conditional display of butt…
jlong1307 Apr 22, 2024
4f7bb8d
[ADD] Chapter 12: add the users_view.xml and the inherited_user file …
jlong1307 Apr 22, 2024
bfb36bf
[ADD] Chapter 13 : Add a new module estate_account, when a property i…
jlong1307 Apr 23, 2024
50765fa
[IMP] Chapter 14 : Add the kanban view in the estate property view xm…
jlong1307 Apr 23, 2024
4e9e3f5
[IMP] chapter 15: Polish my code
jlong1307 Apr 23, 2024
77aa472
[IMP] Chatper 15 : Polish my code with the runbot
jlong1307 Apr 23, 2024
fddc9c7
[IMP] 1.1 Display the counter
jlong1307 Apr 24, 2024
e83c3ba
[ADD 1.2 Add Counter folder in the src with counter xml and counter j…
jlong1307 Apr 24, 2024
1d6e4c4
[ADD] 1.3 Add the card component
jlong1307 Apr 24, 2024
cc45df2
[IMP] 1.4 Using markup to display html. Update Card to use t-out and …
jlong1307 Apr 24, 2024
af77622
[IMP] 1.5 Props validation: Add props validation to the card component
jlong1307 Apr 24, 2024
7f02787
[IMP] 1.6 Add onchange to the counter and the sum of the two incr in …
jlong1307 Apr 24, 2024
a2a34b6
[ADD] 1.7: A todo list : Added the folder todo with the todolist comp…
jlong1307 Apr 25, 2024
acdeb00
[IMP] 1.8 Use dynamic attributs : Add classes to the todoitem
jlong1307 Apr 25, 2024
9a83e9e
[IMP] 10.1.1 : Focus the input without the useAutoFocus
jlong1307 Apr 25, 2024
ff8aebb
[ADD] 10.1.2 : Add the utils files into the todo folder with the useA…
jlong1307 Apr 25, 2024
b126f5f
[IMP] 1.11 : Tggling todos: Add the checkbox in the todoList and add …
jlong1307 Apr 25, 2024
c5d553d
[IMP] add some flex
jlong1307 Apr 25, 2024
c18442f
[IMP] 1.12 Deleting todos: Add the remove in the TodoItem
jlong1307 Apr 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ It has 3 branches for each Odoo version: one for the bases, one for
The first contains the code of the modules that serve as base for the tutorials,
and the others contains the code of each chapter with the complete
solution.


Hello world !
11 changes: 11 additions & 0 deletions awesome_owl/static/src/card/card.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/** @odoo-module **/

import { Component } from "@odoo/owl";

export class Card extends Component{
static template = "awesome_owl.Card";
static props = {
title: String,
content: String,
};
}
15 changes: 15 additions & 0 deletions awesome_owl/static/src/card/card.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="awesome_owl.Card">
<div class="card d-inline-block m-2" style="width: 18rem;">
<div class="card-body">
<h5 class="card-title">
<t t-out="props.title"/>
</h5>
<p class="card-text">
<t t-out="props.content"/>
</p>
</div>
</div>
</t>
</templates>
24 changes: 24 additions & 0 deletions awesome_owl/static/src/counter/counter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/** @odoo-module **/

import { Component, useState } from "@odoo/owl";

export class Counter extends Component{
static template = "awesome_owl.Counter";
static props = {
onChange:{
type: Function,
optional: true,
}
};

setup(){
this.state = useState({value: 0});
}

increment(){
this.state.value++;
if (this.props.onChange){
this.props.onChange();
}
}
}
10 changes: 10 additions & 0 deletions awesome_owl/static/src/counter/counter.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="awesome_owl.Counter">
<div class="m-2 p-2 border d-inline-block">
<span class="me-2">Counter : <t t-esc="state.value"/>
</span>
<button class="btn btn-primary" t-on-click="increment">Increment</button>
</div>
</t>
</templates>
17 changes: 16 additions & 1 deletion awesome_owl/static/src/playground.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
/** @odoo-module **/

import { Component } from "@odoo/owl";
import { Component, markup, useState} from "@odoo/owl";
import { Counter } from "./counter/counter";
import { Card } from "./card/card";
import { TodoList } from "./todo/todoList/todoList";

export class Playground extends Component {
static template = "awesome_owl.playground";

static components = { Counter, Card, TodoList };

setup(){
this.test1 = "<div><p>Some text</p></div>";
this.test2 = markup("<div><p>Some text</p></div>");
this.sum = useState({value:0});
}

incrSum(){
this.sum.value++;
}
}
14 changes: 12 additions & 2 deletions awesome_owl/static/src/playground.xml
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">

<t t-name="awesome_owl.playground">
<div class="p-3">
hello world
<Counter onChange.bind="incrSum"/>
<Counter onChange.bind="incrSum"/>
<div>
The sum is : <t t-esc="sum.value"/>
</div>
</div>
<div>
<Card title="'Card 1'" content="test1"/>
<Card title="'Card 2'" content="test2"/>
</div>
<div>
<TodoList/>
</div>
</t>

</templates>
23 changes: 23 additions & 0 deletions awesome_owl/static/src/todo/todoItem/todoItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/** @odoo-module **/

import { Component } from "@odoo/owl";

export class TodoItem extends Component{
static template = "awesome_owl.TodoItem";
static props = {
todo:{
type: Object,
shape: {id : Number, description: String, flag: Boolean}
},
toggleState: Function,
removeTodo : Function,
};

onChange(){
this.props.toggleState(this.props.todo.id);
}

onRemove(){
this.props.removeTodo(this.props.todo.id)
}
}
13 changes: 13 additions & 0 deletions awesome_owl/static/src/todo/todoItem/todoItem.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="awesome_owl.TodoItem">
<div class="form-check">
<input class="form-check-input" type="checkbox" t-att-id="props.todo.id" t-att-check="props.todo.flag" t-on-change="onChange"/>
<label t-att-for="props.todo.id" t-att-class="props.todo.flag ? 'text-muted text-decoration-line-through' : ''">
<t t-esc="props.todo.id"/>
<t t-esc="props.todo.description"/>
</label>
<span role="button" class="fa fa-remove" t-on-click="onRemove"/>
</div>
</t>
</templates>
44 changes: 44 additions & 0 deletions awesome_owl/static/src/todo/todoList/todoList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/** @odoo-module **/

import { Component, useState, useRef, onMounted } from "@odoo/owl";
import { TodoItem } from "../todoItem/todoItem";
import { useAutofocus } from "../utils";

export class TodoList extends Component {
static template = "awesome_owl.TodoList";

static components = { TodoItem };

setup(){
this.todos = useState([]);
this.id = 0;
useAutofocus("input");
}

addTodo(ev){
if(ev.keyCode === 13 && ev.target.value != "")
{
this.todos.push({
id: this.id++,
description: ev.target.value,
flag: false
});
ev.target.value = "";
}
}

toggleTodo(todoId){
const todo = this.todos.find((todo) => todo.id === todoId);
if (todo){
todo.flag = !todo.flag;
}
}

removeTodo(todoId)
{
const index = this.todos.findIndex((todo) => todo.id === todoId)
if(index >= 0){
this.todos.splice(index, 1);
}
}
}
11 changes: 11 additions & 0 deletions awesome_owl/static/src/todo/todoList/todoList.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="awesome_owl.TodoList">
<div class="d-flex flex-column border p-2 m-2">
<input type="text" id="todoInput" t-on-keyup="addTodo" placeholder="Add a todo" t-ref="input"/>
<t t-foreach="todos" t-as="todo" t-key="todo.id">
<TodoItem todo="todo" toggleState.bind="toggleTodo" removeTodo.bind="removeTodo"/>
</t>
</div>
</t>
</templates>
10 changes: 10 additions & 0 deletions awesome_owl/static/src/todo/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/** @odoo-module */

import { useRef, onMounted } from "@odoo/owl";

export function useAutofocus(refName) {
const ref = useRef(refName);
onMounted(() => {
ref.el.focus();
});
}
1 change: 1 addition & 0 deletions estate/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
13 changes: 13 additions & 0 deletions estate/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "estate",
"depends": ["base"],
"author": "loje",
"data": [
"security/ir.model.access.csv",
"views/estate_property_views.xml",
"views/estate_property_type_views.xml",
"views/estate_property_offer_views.xml",
"views/res_user_views.xml",
"views/estate_property_menus.xml",
],
} # type: ignore
5 changes: 5 additions & 0 deletions estate/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from . import estate_property
from . import estate_property_type
from . import estate_property_tag
from . import estate_property_offer
from . import res_user
132 changes: 132 additions & 0 deletions estate/models/estate_property.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
from datetime import timedelta

from odoo import api, fields, models # type: ignore
from odoo.exceptions import UserError, ValidationError # type: ignore
from odoo.tools.float_utils import float_compare, float_is_zero # type: ignore


class EstateProterty(models.Model):
_name = "estate.property"
_description = "estate property"
_order = "id desc"

name = fields.Char(string="Title", required=True)
description = fields.Text(string="Description")
postcode = fields.Char(string="Postcode")
date_availability = fields.Date(
string="Available From",
default=fields.Date.today() + timedelta(days=90),
copy=False,
)
expected_price = fields.Float(string="Expected Price", required=True)
selling_price = fields.Float(string="Selling Price", copy=False, readonly=True)
bedrooms = fields.Integer(string="Bedrooms", default=2)
living_area = fields.Integer(string="Living Area (sqm)")
facades = fields.Integer(string="Facades")
garage = fields.Boolean(string="Garage")
garden = fields.Boolean(string="Garden", store=True)
garden_area = fields.Integer(string="Garden area (sqm)")
garden_orientation = fields.Selection(
selection=[
("north", "North"),
("south", "South"),
("east", "East"),
("west", "West"),
],
string="Garden Orientation",
)

active = fields.Boolean(default=True)
state = fields.Selection(
selection=[
("new", "New"),
("offer received", "Offer Received"),
("offer accepted", "Offer Accepted"),
("sold", "Sold"),
("canceled", "Canceled"),
],
string="State",
default="new",
copy=False,
required=True,
)
estate_property_type_id = fields.Many2one("estate.property.type", string="Type")
user_id = fields.Many2one(
"res.users",
string="Salesperson",
index=True,
default=lambda self: self.env.user,
)
buyer = fields.Char(string="buyer", copy=False)
buyer_id = fields.Many2one("res.partner", copy=False)
tag_ids = fields.Many2many("estate.property.tag", string="Tags")
offer_ids = fields.One2many("estate.property.offer", "property_id", string="Offer")
total_area = fields.Float(compute="_compute_total_area")
best_price = fields.Float(string="Best offer", compute="_compute_best_price")

@api.depends("offer_ids.price")
def _compute_best_price(self):
for record in self:
record.best_price = max(record.offer_ids.mapped("price"), default=0.0)

@api.depends("living_area", "garden_area")
def _compute_total_area(self):
for record in self:
record.total_area = record.living_area + record.garden_area

@api.onchange("garden")
def _onchange_garden(self):
if self.garden:
self.garden_area = 10
self.garden_orientation = "north"
else:
self.garden_area = 0
self.garden_orientation = False

def action_sold(self):
if self.state == "canceled":
raise UserError("A canceled property cannot be sold")
else:
self.state = "sold"
return True

def action_cancel(self):
if self.state == "sold":
raise UserError("sold property cannot be canceled")
else:
self.state = "canceled"
return True

@api.constrains("expected_price", "selling_price")
def _check_positif(self):
for record in self:
if record.expected_price <= 0:
raise ValidationError("Expected price must be positif")
if record.selling_price < 0:
raise ValidationError("Selling price must be positif")

@api.constrains("expected_price", "selling_price")
def _check_selling_price(self):
for record in self:
if not float_is_zero(
record.selling_price, precision_digits=2
) and not float_is_zero(record.expected_price, precision_digits=2):
if (
float_compare(
record.selling_price,
0.9 * record.expected_price,
precision_digits=2,
)
< 0
):
raise models.ValidationError(
"Selling price cannot be lower than 90% of the expected price."
)

@api.ondelete(at_uninstall=False)
def check_property_state_before_deletion(self):
for record in self:
if record.state not in ["new", "canceled"]:
raise models.ValidationError(
"Cannot delete property with state other than 'New' or 'Canceled'."
)