Files
Yamen Ahmad 3e83d8d7d5 feat: add complete EnCoach backend — Odoo 19 + all addons
Includes:
- Odoo 19 framework (odoo/)
- 27 custom EnCoach addons (new_project/custom_addons/)
  - encoach_core, encoach_api, encoach_lms_api, encoach_adaptive_api
  - encoach_exam, encoach_taxonomy, encoach_adaptive, encoach_assignment
  - encoach_ai, encoach_ai_grading, encoach_ai_generation, encoach_ai_media
  - encoach_courseware, encoach_communication, encoach_subscription
  - encoach_notification, encoach_approval, encoach_branding
  - encoach_classroom, encoach_registration, encoach_stats
  - encoach_faq, encoach_ticket, encoach_training, encoach_resources
  - encoach_adaptive_ai, encoach_sis
- 21 OpenEduCat Enterprise modules (new_project/enterprise-19/)
- 14 OpenEduCat Community modules (new_project/openeducat_erp-19.0/)
- Configuration: odoo.conf, requirements.txt, scripts
- 200+ REST API endpoints with JWT authentication
- SRS and test documentation

Made-with: Cursor
2026-04-01 17:10:04 +04:00

185 lines
6.8 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
import re
from odoo.addons.mail.tests.common import mail_new_test_user
from odoo.addons.product.tests.common import ProductVariantsCommon
class TestStockCommon(ProductVariantsCommon):
"""
This class provides some common resources for stock tests. Most notably, it
provides a dedicated warehouse: `warehouse_1`, along with its own:
* picking types:
* `picking_type_in`
* `picking_type_out`
* `picking_type_int`
* locations:
* `stock_location`
* `pack_location`
* `output_location`
* `shelf_1` and `shelf_2` sublocations
* routes:
* `route_mto`
It also provides references to some resources that are currently globally
available, but might be replaced by custom stock data in the future:
* `customer_location`
* `supplier_location`
* `inter_company_location`
* various product UoMs
There are also dedicated products, users, custom UoMs etc. provided by this
class. See the code below for more details.
Whenever possible, classes inheriting from this one should use the provided
references instead of obtaining global objects on their own. That's because,
in the future, stock tests will be using a dedicated set of test data.
"""
def _create_move(self, product, src_location, dst_location, **values):
# TDE FIXME: user as parameter
Move = self.env['stock.move'].with_user(self.user_stock_manager)
# simulate create + onchange
move = Move.new({'product_id': product.id, 'location_id': src_location.id, 'location_dest_id': dst_location.id})
move_values = move._convert_to_write(move._cache)
move_values.update(**values)
return Move.create(move_values)
@classmethod
def setUpClass(cls):
super().setUpClass()
# Product environment related data
cls.uom_dunit = cls.env['uom.uom'].create({
'name': 'DeciUnit',
'relative_factor': 10.0,
'relative_uom_id': cls.uom_unit.id,
})
cls.product_1, cls.product_2, cls.product_3 = cls.env['product.product'].create([{
'name': 'Courage', # product_1
'type': 'consu',
'default_code': 'PROD-1',
'uom_id': cls.uom_dunit.id,
}, {
'name': 'Wood', # product_2
}, {
'name': 'Stone', # product_3
'uom_id': cls.uom_dozen.id,
}])
cls.ProductObj = cls.env['product.product']
cls.UomObj = cls.env['uom.uom']
cls.PartnerObj = cls.env['res.partner']
cls.ModelDataObj = cls.env['ir.model.data']
cls.StockPackObj = cls.env['stock.move.line']
cls.StockQuantObj = cls.env['stock.quant']
cls.PickingObj = cls.env['stock.picking']
cls.MoveObj = cls.env['stock.move']
cls.LotObj = cls.env['stock.lot']
cls.StockLocationObj = cls.env['stock.location']
# Warehouses
cls.warehouse_1 = cls.env['stock.warehouse'].create({
'name': 'Base Warehouse',
'reception_steps': 'one_step',
'delivery_steps': 'ship_only',
'code': 'BWH',
'sequence': 5,
})
cls.route_mto = cls.warehouse_1.mto_pull_id.route_id
cls.route_mto.rule_ids.procure_method = "make_to_order"
# Model Data
cls.picking_type_in = cls.warehouse_1.in_type_id
cls.picking_type_int = cls.warehouse_1.int_type_id
cls.picking_type_out = cls.warehouse_1.out_type_id
cls.picking_type_out.reservation_method = 'manual'
cls.stock_location = cls.warehouse_1.lot_stock_id
cls.scrap_location = cls.StockLocationObj.search([
('company_id', '=', cls.warehouse_1.company_id.id), ('usage', '=', 'inventory')
], limit=1)
cls.shelf_1, cls.shelf_2 = cls.StockLocationObj.create([{
'name': 'Shelf 1',
'location_id': cls.stock_location.id,
}, {
'name': 'Shelf 2',
'location_id': cls.stock_location.id,
}])
pack_location = cls.warehouse_1.wh_pack_stock_loc_id
pack_location.active = True
cls.pack_location = pack_location
output_location = cls.warehouse_1.wh_output_stock_loc_id
output_location.active = True
cls.output_location = output_location
cls.supplier_location = cls.quick_ref('stock.stock_location_suppliers')
cls.customer_location = cls.quick_ref('stock.stock_location_customers')
cls.inter_company_location = cls.quick_ref('stock.stock_location_inter_company')
# Product Created A, B, C, D
(
cls.productA,
cls.productB,
cls.productC,
cls.productD,
cls.productE,
) = cls.ProductObj.create([
{'name': 'Product A', 'is_storable': True},
{'name': 'Product B', 'is_storable': True},
{'name': 'Product C', 'is_storable': True},
{'name': 'Product D', 'is_storable': True},
{'name': 'Product E', 'is_storable': True},
])
# Configure unit of measure.
cls.uom_kg = cls.uom_kgm
cls.uom_gm = cls.uom_gram
cls.kgB, cls.gB = cls.ProductObj.create([
{'name': 'kg-B', 'is_storable': True, 'uom_id': cls.uom_kg.id},
{'name': 'g-B', 'is_storable': True, 'uom_id': cls.uom_gm.id}
])
cls.group_user.write({'implied_ids': [
(4, cls.quick_ref('base.group_multi_company').id),
(4, cls.quick_ref('stock.group_production_lot').id),
]})
# User Data: stock user and stock manager
cls.user_stock_user = mail_new_test_user(
cls.env,
name='Pauline Poivraisselle',
login='pauline',
email='p.p@example.com',
notification_type='inbox',
groups='stock.group_stock_user',
)
cls.user_stock_manager = mail_new_test_user(
cls.env,
name='Julie Tablier',
login='julie',
email='j.j@example.com',
notification_type='inbox',
groups='stock.group_stock_manager',
)
# Partner
cls.partner_1 = cls.env['res.partner'].create({
'name': 'Julia Agrolait',
'email': 'julia@agrolait.example.com',
})
# Existing data
cls.existing_inventories = cls.StockQuantObj.search([('inventory_quantity', '!=', 0.0)])
cls.existing_quants = cls.StockQuantObj.search([])
def url_extract_rec_id_and_model(self, url):
# Extract model and record ID
action_match = re.findall(r'action-([^/]+)', url)
model_name = self.env.ref(action_match[0]).res_model
rec_id = re.findall(r'/(\d+)$', url)[0]
return rec_id, model_name