from .. import controller from odoo import http from odoo.http import request import ast, json class Category(controller.Controller): prefix = '/api/v1/' @http.route(prefix + 'category/child', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() def get_category_child(self, **kw): params = self.get_request_params(kw, { 'parent_id': ['number', 'default:0'] }) if not params['valid']: return self.response(code=400, description=params) if params['value']['parent_id'] == 0: params['value']['parent_id'] = False parent_id = params['value'].get('parent_id', 0) if parent_id == 0: parent_id = False categories = request.env['product.public.category'].search_read( [('parent_frontend_id', '=', parent_id)], ['id', 'name', 'image_1920'] ) for category in categories: category['image_1920'] = request.env['ir.attachment'].api_image( 'product.public.category', 'image_1920', category['id'] ) return self.response(categories) @http.route(prefix + 'category/numFound', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() def get_category_numfound(self, **kw): params = self.get_request_params(kw, { 'parent_id': ['number', 'default:0'] }) if not params['valid']: return self.response(code=400, description=params) if params['value']['parent_id'] == 0: params['value']['parent_id'] = False parent_id = params['value'].get('parent_id', 0) if parent_id == 0: parent_id = False def get_category_level(category_id): """Return the level of the category based on its parent hierarchy.""" category = request.env['product.public.category'].browse(category_id) level = 0 while category.parent_frontend_id: category = category.parent_frontend_id level += 1 return level def compute_num_found(category, depth, start_depth): """Recursively compute the total number of product templates in the category and its children starting from the given depth.""" if depth > 2: return 0 child_categories = request.env['product.public.category'].search([('parent_frontend_id', '=', category.id)]) # Fetch products that are not unpublished num_found = len(category.product_tmpl_ids.filtered(lambda p: not p.unpublished)) for child in child_categories: num_found += compute_num_found(child, depth + 1, start_depth) return num_found # Fetch the category and determine its level category = request.env['product.public.category'].browse(parent_id) category_level = get_category_level(category.id) if category else -1 if category: start_depth = category_level category_data = { 'id': category.id, 'name': category.name, } if category_level == 0: # Level 1 category, fetch child level 2 and their children level 3 category_data['numFound'] = compute_num_found(category, 0, start_depth) child_categories_level_2 = request.env['product.public.category'].search_read( [('parent_frontend_id', '=', category.id)], ['id', 'name'] ) for child in child_categories_level_2: child_record = request.env['product.public.category'].browse(child['id']) child['numFound'] = compute_num_found(child_record, 1, start_depth) child['children'] = request.env['product.public.category'].search_read( [('parent_frontend_id', '=', child['id'])], ['id', 'name'] ) for grandchild in child['children']: grandchild_record = request.env['product.public.category'].browse(grandchild['id']) grandchild['numFound'] = compute_num_found(grandchild_record, 2, start_depth) category_data['children'] = child_categories_level_2 elif category_level == 1: # Level 2 category, fetch child level 3 category_data['numFound'] = compute_num_found(category, 1, start_depth) child_categories_level_3 = request.env['product.public.category'].search_read( [('parent_frontend_id', '=', category.id)], ['id', 'name'] ) for child in child_categories_level_3: child_record = request.env['product.public.category'].browse(child['id']) child['numFound'] = compute_num_found(child_record, 2, start_depth) category_data['children'] = child_categories_level_3 else: # Level 3 category or deeper, no children category_data['numFound'] = compute_num_found(category, 2, start_depth) category_data['children'] = [] response_data = category_data else: response_data = {} return self.response(response_data) class CategoryManagement(controller.Controller): prefix = '/api/v1/' @http.route(prefix + 'category/tree', auth='public', methods=['GET', 'OPTIONS'], csrf=False) @controller.Controller.must_authorized() def get_category_tree(self): parent_categories = request.env['product.public.category'].search_read([('parent_frontend_id', '=', False)], ['id', 'name', 'image_1920']) data = [] for parent_category in parent_categories: category_data_ids = [parent_category['id']] parent_data = { 'id': parent_category['id'], 'name': parent_category['name'], 'image': request.env['ir.attachment'].sudo().api_image('product.public.category', 'image_1920', parent_category['id']), 'category_data_ids': category_data_ids, 'childs': [] } child_1_categories = request.env['product.public.category'].search_read( [('parent_frontend_id', '=', parent_category['id'])], ['id', 'name']) for child_1_category in child_1_categories: child_1_data = { 'id': child_1_category['id'], 'name': child_1_category['name'], 'childs': [] } category_data_ids.append(child_1_category['id']) child_2_categories = request.env['product.public.category'].search_read( [('parent_frontend_id', '=', child_1_category['id'])], ['id', 'name']) for child_2_category in child_2_categories: child_2_data = { 'id': child_2_category['id'], 'name': child_2_category['name'], } child_1_data['childs'].append(child_2_data) category_data_ids.append(child_2_category['id']) parent_data['childs'].append(child_1_data) parent_data['category_data_ids'] = category_data_ids data.append(parent_data) return self.response(data, headers=[('Cache-Control', 'max-age=3600, public')]) @http.route(prefix + 'categories_homepage/ids', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() def get_categories_homepage_count(self): query = [('status', '=', 'tayang')] categories = request.env['website.categories.homepage'].search_read(query, ['id'], order="sequence") return self.response([x['id'] for x in categories]) # TODO: Delete this function after unused by website @http.route(prefix + 'categories_homepage', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() def get_categories_homepage(self, **kw): base_url = request.env['ir.config_parameter'].get_param('web.base.url') query = [('status', '=', 'tayang')] id = kw.get('id') if id: query.append(('id', '=', id)) categories = request.env['website.categories.homepage'].search(query, order='sequence') data = [] for category in categories: products = category.product_ids data.append({ 'id': category.id, 'sequence': category.sequence, 'category_id': category.category_id.id, 'name': category.category_id.name, 'image': request.env['ir.attachment'].api_image('website.categories.homepage', 'image', category.id), 'url': category.url, 'products': [request.env['product.template'].api_single_response(x) for x in products] }) return self.response(data, headers=[('Cache-Control', 'max-age=3600, public')]) @http.route(prefix + 'category/page/', auth='public', methods=['GET']) @controller.Controller.must_authorized() def get_category(self, **kw): category_ids = [] page = kw.get('page') if page == 'flash-sale': active_flash_sale = request.env['product.pricelist'].get_active_flash_sale() if active_flash_sale: category_ids = [x.id for item in active_flash_sale.item_ids for x in item.product_id.public_categ_ids] elif page == 'ready-stock': product_templates = request.env['product.template'].search([('virtual_qty', '>', 0)]) if product_templates: category_ids = [x.id for product in product_templates for x in product.public_categ_ids] elif page == 'manufacture': manufacture_id = kw.get('manufacture_id') if not manufacture_id: return self.response(code=400, description='manufacture_id is required') product_variants = request.env['product.product'].search([('x_manufacture', '=', int(manufacture_id))]) category_ids = [x.id for variant in product_variants for x in variant.public_categ_ids] elif page == 'promotion': promotion_id = kw.get('promotion_id') if not promotion_id: return self.response(code=400, description='promotion_id is required') coupon_program = request.env['coupon.program'].search([('id', '=', promotion_id)]) product_variants = request.env['product.product'].search(ast.literal_eval(coupon_program.rule_products_domain)) category_ids = [x.id for variant in product_variants for x in variant.public_categ_ids] else: return self.response(code=400, description='page possible value is flash-sale, ready-stock, manufacture, promotion') categories = request.env['product.public.category'].search([('id', 'in', category_ids)]) data = [] for category in categories: data.append({ 'id': category.id, 'name': category.name }) return self.response(data) @http.route(prefix + 'category//category-breadcrumb', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() def get_category_parents_by_id(self, id, **kw): categories = [] category = request.env['product.public.category'].search([('id', '=', int(id))], limit=1) if not category: return self.response(code=400, description='Category not found') while category: categories.append({'id': category.id, 'name': category.name}) category = category.parent_frontend_id categories.reverse() return self.response(categories, headers=[('Cache-Control', 'max-age=3600, public')]) @http.route(prefix + 'category//short-desc', auth='public', methods=['GET', 'OPTIONS']) @controller.Controller.must_authorized() def get_category_short_desc(self, id, **kw): category = request.env['product.public.category'].browse(id) if not category.exists(): return self.response(code=400, description='Category not found') return self.response({ "id": category.id, "short_desc": category.short_desc or "" }, headers=[('Cache-Control', 'max-age=600, public')])