mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-21 03:12:10 +02:00
193 lines
5.8 KiB
JavaScript
193 lines
5.8 KiB
JavaScript
/** @odoo-module **/
|
|
|
|
import { useBus, useService } from "@web/core/utils/hooks";
|
|
import { SEARCH_KEYS } from "@web/search/with_search/with_search";
|
|
import { buildSampleORM } from "@web/views/sample_server";
|
|
import { useSetupView } from "@web/views/view_hook";
|
|
|
|
import { EventBus, onMounted, onWillStart, onWillUpdateProps, status, useComponent } from "@odoo/owl";
|
|
|
|
/**
|
|
* @typedef {import("@web/search/search_model").SearchParams} SearchParams
|
|
*/
|
|
|
|
export class Model extends EventBus {
|
|
/**
|
|
* @param {Object} env
|
|
* @param {Object} services
|
|
*/
|
|
constructor(env, params, services) {
|
|
super();
|
|
this.env = env;
|
|
this.orm = services.orm;
|
|
this.useSampleModel = false; // will be set to true by the "useModel" hook if necessary
|
|
this.setup(params, services);
|
|
}
|
|
|
|
/**
|
|
* @param {Object} params
|
|
* @param {Object} services
|
|
*/
|
|
setup(/* params, services */) {}
|
|
|
|
/**
|
|
* @param {SearchParams} searchParams
|
|
*/
|
|
async load(/* searchParams */) {}
|
|
|
|
/**
|
|
* This function is meant to be overriden by models that want to implement
|
|
* the sample data feature. It should return true iff the last loaded state
|
|
* actually contains data. If not, another load will be done (if the sample
|
|
* feature is enabled) with the orm service substituted by another using the
|
|
* SampleServer, to have sample data to display instead of an empty screen.
|
|
*
|
|
* @returns {boolean}
|
|
*/
|
|
hasData() {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* This function is meant to be overriden by models that want to combine
|
|
* sample data with real groups that exist on the server.
|
|
*
|
|
* @returns {boolean}
|
|
*/
|
|
getGroups() {
|
|
return null;
|
|
}
|
|
|
|
notify() {
|
|
this.trigger("update");
|
|
}
|
|
}
|
|
Model.services = [];
|
|
|
|
/**
|
|
* @param {Object} props
|
|
* @returns {SearchParams}
|
|
*/
|
|
function getSearchParams(props) {
|
|
const params = {};
|
|
for (const key of SEARCH_KEYS) {
|
|
params[key] = props[key];
|
|
}
|
|
return params;
|
|
}
|
|
|
|
/**
|
|
* @template {Model} T
|
|
* @param {new (env: Object, params: Object, services: Object) => T} ModelClass
|
|
* @param {Object} params
|
|
* @param {Object} [options]
|
|
* @param {Function} [options.onUpdate]
|
|
* @returns {T}
|
|
*/
|
|
export function useModel(ModelClass, params, options = {}) {
|
|
const component = useComponent();
|
|
if (!(ModelClass.prototype instanceof Model)) {
|
|
throw new Error(`the model class should extend Model`);
|
|
}
|
|
const services = {};
|
|
for (const key of ModelClass.services) {
|
|
services[key] = useService(key);
|
|
}
|
|
services.orm = services.orm || useService("orm");
|
|
if (services.dialog) {
|
|
services.dialog = Object.create(services.dialog);
|
|
const dialogAddOrigin = services.dialog.add;
|
|
let dialogRequests = [];
|
|
services.dialog.add = (...args) => {
|
|
const index = dialogRequests.push(args);
|
|
return () => {
|
|
dialogRequests[index] = null;
|
|
}
|
|
}
|
|
onMounted(() => {
|
|
services.dialog.add = dialogAddOrigin;
|
|
for (const req of dialogRequests) {
|
|
if (req) {
|
|
dialogAddOrigin(...req);
|
|
}
|
|
}
|
|
dialogRequests = null;
|
|
});
|
|
}
|
|
|
|
if (!("isAlive" in params)) {
|
|
params.isAlive = () => status(component) !== "destroyed";
|
|
}
|
|
|
|
const model = new ModelClass(component.env, params, services);
|
|
useBus(
|
|
model,
|
|
"update",
|
|
options.onUpdate ||
|
|
(() => {
|
|
component.render(true); // FIXME WOWL reactivity
|
|
})
|
|
);
|
|
|
|
const globalState = component.props.globalState || {};
|
|
let useSampleModel =
|
|
component.props.useSampleModel &&
|
|
(!("useSampleModel" in globalState) || globalState.useSampleModel);
|
|
model.useSampleModel = !options.ignoreUseSampleModel ? useSampleModel : false;
|
|
const orm = model.orm;
|
|
let sampleORM = globalState.sampleORM;
|
|
const user = useService("user");
|
|
let started = false;
|
|
async function load(props) {
|
|
const searchParams = getSearchParams(props);
|
|
await model.load(searchParams);
|
|
if (!options.ignoreUseSampleModel) {
|
|
if (useSampleModel && !model.hasData()) {
|
|
sampleORM =
|
|
sampleORM ||
|
|
buildSampleORM(component.props.resModel, component.props.fields, user);
|
|
sampleORM.setGroups(model.getGroups());
|
|
// Load data with sampleORM then restore real ORM.
|
|
model.orm = sampleORM;
|
|
await model.load(searchParams);
|
|
model.orm = orm;
|
|
} else {
|
|
useSampleModel = false;
|
|
model.useSampleModel = useSampleModel;
|
|
}
|
|
}
|
|
if (started) {
|
|
model.notify();
|
|
}
|
|
}
|
|
onWillStart(async () => {
|
|
// FIXME: we have a problem here: in the view, we have two onWillStart:
|
|
// - 1) to load the subviews that aren't inline
|
|
// - 2) to load the data
|
|
// 2) must be done after 1), but we can't sync two onWillStarts
|
|
// The problem is also there with the relational model, but it isn't visible
|
|
// in the tests because the load the sub views in a tick, and we look inside
|
|
// the fieldsInfo after a tick as well. Here, we look into fieldsInfo directly.
|
|
if (params.beforeLoadProm) {
|
|
await params.beforeLoadProm;
|
|
}
|
|
await load(component.props);
|
|
started = true;
|
|
});
|
|
onWillUpdateProps((nextProps) => {
|
|
if (!options.ignoreUseSampleModel) {
|
|
useSampleModel = false;
|
|
}
|
|
load(nextProps);
|
|
});
|
|
|
|
useSetupView({
|
|
getGlobalState() {
|
|
if (component.props.useSampleModel) {
|
|
return { sampleORM, useSampleModel };
|
|
}
|
|
},
|
|
});
|
|
|
|
return model;
|
|
}
|