Muhammad Osama
Muhammad Osama

Reputation: 1

How to override java script base addon file to custom addon in odoo 16

This is the js file in "documents" enterprise base addon I just want to add "documetns_type_id" in here export const inspectorFields = [] through custom addon.

/** @odoo-module **/

import { device } from "web.config";
import { str_to_datetime } from "web.time";
import { session } from "@web/session";
import { KeepLast } from "@web/core/utils/concurrency";
import { intersection } from "@web/core/utils/arrays";
import { AutoComplete } from "@web/core/autocomplete/autocomplete";
import { x2ManyCommands } from "@web/core/orm_service";
import { useBus, useService } from "@web/core/utils/hooks";
import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog";
import { FileUploader } from "@web/views/fields/file_handler";
import { ChatterContainer } from "@mail/components/chatter_container/chatter_container";
import { DocumentsInspectorField } from "./documents_inspector_field";
import { download } from "@web/core/network/download";
import { onNewPdfThumbnail } from "../helper/documents_pdf_thumbnail_service";
import { useTriggerRule } from "@documents/views/hooks";

const { Component, markup, useEffect, useState, useRef, onPatched, onWillUpdateProps, onWillStart } = owl;

async function toggleArchive(model, resModel, resIds, doArchive) {
    const method = doArchive ? "action_archive" : "action_unarchive";
    const action = await model.orm.call(resModel, method, [resIds]);
    if (action && Object.keys(action).length !== 0) {
        model.action.doAction(action);
    }
}

export const inspectorFields = [
    "attachment_id",
    "active",
    "activity_ids",
    "available_rule_ids",
    "checksum",
    "display_name",
    "folder_id",
    "thumbnail_status",
    "lock_uid",
    "message_attachment_count",
    "message_follower_ids",
    "message_ids",
    "mimetype",
    "name",
    "owner_id",
    "partner_id",
    "previous_attachment_ids",
    "res_id",
    "res_model",
    "res_model_name",
    "res_name",
    "tag_ids",
    "type",
    "url",
    "file_size",
];

export class DocumentsInspector extends Component {
    setup() {
        this.orm = useService("orm");
        this.action = useService("action");
        this.dialogService = useService("dialog");
        this.notificationService = useService("notification");
        this.documentsReplaceInput = useRef("replaceFileInput");
        this.chatterContainer = useRef("chatterContainer");
        this.keepLast = new KeepLast();
        this.previewLockCount = 0;
        this.str_to_datetime = str_to_datetime;
        const { triggerRule } = useTriggerRule();
        this._triggerRule = triggerRule;
        const { bus: fileUploadBus } = useService("file_upload");
        useBus(fileUploadBus, "FILE_UPLOAD_LOADED", (ev) => {
            let documentId = ev.detail.upload.data.get("document_id");
            if (documentId && this.resIds.includes(Number.parseInt(documentId))) {
                this.state.previousAttachmentDirty = true;
            }
        });

        // Avoid generating new urls if they were generated within this component's lifetime
        this.generatedUrls = {};
        this.state = useState({
            previousAttachmentData: null,
            previousAttachmentDirty: true,
            showChatter: this.isMobile,
        });
        const updateLockedState = (props) => {
            this.isLocked =
                (props.selection.find((rec) => rec.data.lock_uid && rec.data.lock_uid[0] !== session.uid) && true) ||
                false;
            const folderIds = props.selection.map((rec) => rec.data.folder_id[0]);
            const folders = this.env.searchModel.getFolders().filter((folder) => folderIds.includes(folder.id));
            this.isEditDisabled = !!folders.find((folder) => !folder.has_write_access);
        };
        onWillStart(() => {
            updateLockedState(this.props);
            this.updateAttachmentHistory(null);
        });
        onWillUpdateProps((nextProps) => {
            this.generatedUrl = false;
            updateLockedState(nextProps);
            this.updateAttachmentHistory(nextProps);
        });

        // Chatter
        const chatterCloseHandler = () => {
            this.state.showChatter = this.isMobile;
        };
        const chatterReloadHandler = async () => {
            const record = this.props.selection[0];
            if (!record) {
                return;
            }
            await record.load();
            await record.model.notify();
        };
        useEffect(
            (el) => {
                if (!el) {
                    return;
                }
                el.addEventListener("o-close-chatter", chatterCloseHandler);
                el.addEventListener("reload", chatterReloadHandler);
                return () => {
                    el.removeEventListener("o-close-chatter", chatterCloseHandler);
                    el.removeEventListener("reload", chatterReloadHandler);
                };
            },
            () => [this.chatterContainer.el && this.chatterContainer.el.querySelector(".o_Chatter")]
        );

        // Pdf thumbnails
        if (this.props.withFilePreview) {
            this.pdfService = useService("documents_pdf_thumbnail");
            onWillStart(async () => {
                this.pdfService.enqueueRecords(this.props.selection);
            })
            onWillUpdateProps(async (nextProps) => {
                this.pdfService.enqueueRecords(nextProps.selection);
            })
            onNewPdfThumbnail(({ detail }) => {
                if (this.props.selection.find(rec => rec.resId === detail.record.resId)) {
                    this.render(true);
                }
            });
        }

        //Mobile specific
        if (!this.env.isSmall) {
            return;
        }
        this.inspectorMobileRef = useRef("inspectorMobile");
        this.shouldOpenInspector = false;
        onWillUpdateProps((nextProps) => {
            // Only open the inspector if there is only one selected element and
            //  it was not previously selected.
            this.shouldOpenInspector = nextProps.selection.length === 1;
        });
        onPatched(() => {
            if (!this.inspectorMobileRef.el) {
                return;
            }
            if (this.shouldOpenInspector) {
                this.inspectorMobileRef.el.setAttribute("open", "");
            }
        });
    }

    get resIds() {
        return this.props.selection.map((rec) => rec.resId);
    }

    get isMobile() {
        return this.env.isSmall;
    }

    updateAttachmentHistory(nextProps) {
        const props = nextProps || this.props;
        const record = props.selection[0];
        if (props.selection.length !== 1) {
            this.state.showChatter = this.isMobile;
        }
        if (!record || props.selection.length !== 1 || !record.data.previous_attachment_ids.count) {
            this.keepLast.add(Promise.resolve());
            this.state.previousAttachmentData = null;
            return;
        }
        const previousRecord = this.props.selection.length === 1 && this.props.selection[0];
        if (
            nextProps &&
            previousRecord &&
            previousRecord.resId === record.resId &&
            !this.state.previousAttachmentDirty
        ) {
            return;
        }
        this.keepLast.add(
            this.orm
                .searchRead(
                    "ir.attachment",
                    [["id", "in", record.data.previous_attachment_ids.records.map((rec) => rec.resId)]],
                    ["name", "create_date", "create_uid"],
                    {
                        order: "create_date desc",
                    }
                )
                .then((result) => {
                    this.state.previousAttachmentData = result;
                    this.state.previousAttachmentDirty = false;
                })
        );
    }

    getCurrentFolder() {
        return this.env.searchModel.getSelectedFolder();
    }

    getFolderDescription() {
        return markup(this.getCurrentFolder().description);
    }

    /**
     * Returns an object with additional data for our record
     */
    getRecordAdditionalData(record) {
        const additionalData = {
            isGif: new RegExp("image.*(gif)").test(record.data.mimetype),
            isImage: new RegExp("image.*(jpeg|jpg|png)").test(record.data.mimetype),
            isYoutubeVideo: false,
            youtubeToken: undefined,
        };
        if (record.data.url && record.data.url.length) {
            const youtubeUrlMatch = record.data.url.match(
                "youtu(?:.be|be.com)/(?:.*v(?:/|=)|(?:.*/)?)([a-zA-Z0-9-_]{11})"
            );
            if (youtubeUrlMatch && youtubeUrlMatch.length > 1) {
                additionalData.isYoutubeVideo = true;
                additionalData.youtubeToken = youtubeUrlMatch[1];
            }
        }
        return additionalData;
    }

    /**
     * Returns the classes to give to the file preview
     */
    getPreviewClasses(record, additionalData) {
        const nbPreviews = this.props.selection.length;
        const classes = ["o_document_preview"];
        if (record.data.type === "empty") {
            classes.push("o_document_request_preview");
        }
        if (nbPreviews === 1) {
            classes.push("o_documents_single_preview");
        }
        if (additionalData.isImage || additionalData.isYoutubeVideo || (record.isPdf() && record.hasThumbnail())) {
            classes.push("o_documents_preview_image");
        } else {
            classes.push("o_documents_preview_mimetype");
        }
        if (additionalData.isYoutubeVideo || additionalData.isGif) {
            classes.push("o_non_image_preview");
        }
        return classes.join(" ");
    }

    isPdfOnly() {
        return this.props.selection.every((record) => record.isPdf());
    }

    download(records) {
        if (records.length === 1) {
            download({
                data: {},
                url: `/documents/content/${records[0].resId}`,
            });
        } else {
            download({
                data: {
                    file_ids: records.map(rec => rec.resId),
                    zip_name: `documents-${moment().format("YYYY-MM-DD")}.zip`,
                },
                url: "/document/zip",
            });
        }
    }

    onDownload() {
        if (!this.props.selection.length) {
            return;
        }
        this.download(this.props.selection);
    }

    // Override during tests.
    _writeInClipboard(text) {
        navigator.clipboard.writeText(text);
    }

    async onShare() {
        const resIds = this.resIds;
        if (!this.generatedUrls[resIds]) {
            this.generatedUrls[resIds] = await this.orm.call(
                "documents.share",
                "action_get_share_url",
                [{
                    document_ids: [x2ManyCommands.replaceWith(this.resIds)],
                    folder_id: this.env.searchModel.getSelectedFolderId(),
                    type: "ids",
                }],
            );
        }
        this._writeInClipboard(this.generatedUrls[resIds]);
        this.notificationService.add(
            this.env._t("The share url has been copied to your clipboard."),
            {
                type: "success",
            },
        );
    }

    async onReplace(ev) {
        if (!ev.target.files.length) {
            return;
        }
        const record = this.props.selection[0];
        await this.env.documentsView.bus.trigger("documents-upload-files", {
            files: ev.target.files,
            folderId: this.env.searchModel.getSelectedFolderId() || (record.data.folder_id && record.data.folder_id[0]),
            recordId: this.props.selection[0].resId,
            tagIds: this.env.searchModel.getSelectedTagIds(),
        });
        ev.target.value = "";
    }

    async onLock() {
        await this.doLockAction(async () => {
            const record = this.props.selection[0];
            await this.orm.call("documents.document", "toggle_lock", this.resIds);
            await record.load();
            await record.model.notify();
        });
    }

    async _toggleArchive(state) {
        const record = this.props.selection[0];
        await toggleArchive(record.model, record.resModel, this.resIds, state);
        await record.model.load();
        await record.model.notify();
    }

    async onArchive() {
        await this._toggleArchive(true);
    }

    async onUnarchive() {
        await this._toggleArchive(false);
    }

    async onDelete() {
        await this.props.selection[0].model.root.deleteRecords(this.props.selection);
    }

    getFieldProps(fieldName, additionalProps) {
        const props = {
            record: this.props.selection[0],
            name: fieldName,
            selection: this.props.selection,
            inspectorReadonly: this.isLocked || this.isEditDisabled,
            lockAction: this.doLockAction.bind(this),
        };
        if (additionalProps) {
            Object.assign(props, additionalProps);
        }
        return props;
    }

    _getCommonM2M(field) {
        const selection = this.props.selection;
        let commonData = selection[0].data[field].records.map((rec) => rec.resId);
        for (let idx = 1; idx < selection.length; idx++) {
            if (commonData.length === 0) {
                break;
            }
            commonData = intersection(
                commonData,
                selection[idx].data[field].records.map((rec) => rec.resId)
            );
        }
        return commonData.map((id) => selection[0].data[field].records.find((data) => data.resId === id));
    }

    getCommonTags() {
        const searchModelTags = this.env.searchModel.getTags().reduce((res, tag) => {
            res[tag.id] = tag;
            return res;
        }, {});
        return this._getCommonM2M("tag_ids")
            .filter((rec) => searchModelTags[rec.resId])
            .map((rec) => {
                const tag = searchModelTags[rec.resId];
                return {
                    id: rec.resId,
                    name: tag.display_name,
                    group_name: tag.group_name,
                };
            });
    }

    getCommonRules() {
        let commonRules = this._getCommonM2M("available_rule_ids");
        if (this.props.selection.length > 1) {
            commonRules = commonRules.filter((rule) => !rule.data.limited_to_single_record);
        }
        return commonRules;
    }

    getAdditionalTags(commonTags) {
        return this.env.searchModel.getTags().filter((tag) => {
            return !commonTags.find((cTag) => cTag.id === tag.id);
        });
    }

    async removeTag(tag) {
        const record = this.props.selection[0];
        record.model.root._multiSave(record, {
            tag_ids: [x2ManyCommands.forget(tag.id)],
        });
    }

    async addTag(tag, { input }) {
        const record = this.props.selection[0];
        record.model.root._multiSave(record, {
            tag_ids: [x2ManyCommands.linkTo(tag.value)],
        });
        input.focus();
    }

    getTagAutocompleteProps(additionalTags) {
        return {
            value: "",
            onSelect: this.addTag.bind(this),
            sources: [
                {
                    options: (request) => {
                        request = request.toLowerCase();
                        return additionalTags
                            .filter((tag) =>
                                (tag.group_name + " > " + tag.display_name).toLowerCase().includes(request)
                            )
                            .map((tag) => {
                                return {
                                    id: tag.id,
                                    value: tag.id,
                                    label: tag.group_name + " > " + tag.display_name,
                                };
                            });
                    },
                },
            ],
            placeholder: this.env._t(" + Add a tag"),
        };
    }

    async onClickResModel() {
        const record = this.props.selection[0];
        const action = await this.orm.call(record.data.res_model, "get_formview_action", [[record.data.res_id]], {
            context: record.model.user.context,
        });
        await this.action.doAction(action);
    }

    async triggerRule(rule) {
        await this._triggerRule(
            this.props.selection.map(rec => rec.resId),
            rule.resId,
        );
    }

    async onDeletePreviousAttachment(attachmentId) {
        if (this.deleting) {
            return;
        }
        await this.doLockAction(async () => {
            this.deleting = true;
            await this.orm.unlink("ir.attachment", [attachmentId]);
            const record = this.props.selection[0];
            const model = this.props.selection[0].model;
            await record.load();
            this.state.previousAttachmentDirty = true;
            await model.notify();
            this.deleting = false;
        });
    }

    async onDownloadPreviousAttachment(attachmentId) {
        window.location = `/web/content/${attachmentId}?download=true`;
    }

    async onRestorePreviousAttachment(attachmentId) {
        const record = this.props.selection[0];
        await this.doLockAction(async () => {
            await this.orm.write("documents.document", [record.resId], {
                attachment_id: attachmentId,
            });
            await record.load();
            this.state.previousAttachmentDirty = true;
            await record.model.notify();
        });
    }

    openPreview(mainDocument = false, isPdfSplit = false) {
        if ((isPdfSplit && !this.isPdfOnly()) || this.previewLockCount) {
            return;
        }
        const documents = this.props.selection.filter(rec => rec.isViewable());
        this.env.documentsView.bus.trigger("documents-open-preview", {
            documents: documents,
            mainDocument: mainDocument || documents[0],
            isPdfSplit,
            rules: this.getCommonRules(),
            hasPdfSplit: !this.isLocked && !this.isEditDisabled,
        });
    }

    async onEditModel() {
        const record = this.props.selection[0];
        let defaultResourceRef = false;
        if (record.data.res_model && record.data.res_id) {
            defaultResourceRef = `${record.data.res_model},${record.data.res_id}`;
        }
        const models = await this.orm.searchRead("ir.model", [["model", "=", record.data.res_model]], ["id"], {
            limit: 1,
        });
        this.action.doAction(
            {
                name: this.env._t("Edit the linked record"),
                type: "ir.actions.act_window",
                res_model: "documents.link_to_record_wizard",
                views: [[false, "form"]],
                target: "new",
                context: {
                    default_document_ids: [record.resId],
                    default_resource_ref: defaultResourceRef,
                    default_is_readonly_model: true,
                    default_model_id: models[0].id,
                },
            },
            {
                onClose: async () => {
                    await record.model.load();
                    record.model.notify();
                },
            }
        );
    }

    onDeleteModel() {
        const recordId = this.props.selection[0].resId;
        const model = this.props.selection[0].model;
        this.dialogService.add(ConfirmationDialog, {
            body: this.env._t("Do you really want to unlink this record?"),
            confirm: async () => {
                await this.orm.call("documents.workflow.rule", "unlink_record", [[recordId]]);
                await model.load();
                model.notify();
            },
        });
    }

    async doLockAction(func) {
        this.previewLockCount++;
        await func();
        this.previewLockCount--;
    }
}

DocumentsInspector.components = {
    AutoComplete,
    ChatterContainer,
    DocumentsInspectorField,
    FileUploader,
};

if (device.isMobile) {
    DocumentsInspector.template = "documents.DocumentsInspectorMobile";
} else {
    DocumentsInspector.template = "documents.DocumentsInspector";
}

I have created a view "assets.xml" in my custom addons ateam_adaptations/views/assets.xml

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <template id="assets_backend" name="documents assets backend" inherit_id="web.assets_backend">
            <xpath expr="." position="inside">
                <script type="text/javascript"
                        src="/ateam_adaptations/static/src/views/inspector/documents_inspector.js"/>
            </xpath>
        </template>
</odoo>

and this is how i am overriding the js file through custom addons ateam_adaptations/static/src/views/inspector/documents_inspector.js:

odoo.define('ateam_adaptations.documents_inspector', function (require) {
    "use strict";

    console.log("documents_inspector_extension");

    var DocumentsInspector = require('@documents/static/src/views/inspector/documents_inspector.js');
    console.log("DocumentsInspector", DocumentsInspector);

    // Extend the inspectorFields array to include "document_type_id"
    DocumentsInspector.inspectorFields.push("document_type_id");

    return DocumentsInspector;
});

Upvotes: 0

Views: 559

Answers (2)

Muhammad Osama
Muhammad Osama

Reputation: 1

I got the answer of this. In this custom module ateam_adaptations, I add a new js file named documents_inspector.js in this path "ateam_adaptations/static/src/views/inspector/documents_inspector.js" In this js file, I just add the code below:

odoo.define('ateam_adaptations.documents_inspector', async function(require) {
'use strict';
let __exports = {};
const {inspectorFields} = require("@documents/views/inspector/documents_inspector");

inspectorFields.push("document_type_id");

return __exports; 
});

and after that, just add the js file in my manifest.py file:

'assets': {
'web.assets_backend': [
'ateam_adaptations/static/src/views/inspector/documents_inspector.js' ] }

and it worked for me.

Upvotes: 0

Amin Dehghani
Amin Dehghani

Reputation: 59

I am assuming that you are using Odoo v16

in your ateam_adaptations/static/src/views/inspector/documents_inspector.js file just add the code below:

/** @odoo-module **/
import {inspectorFields} from "@documents/views/inspector/documents_inspector";
inspectorFields.push("document_type_id");

and that's it!

for loading your custom JS file the assets.xml file is not used anymore and it has been moved to manifest.py file

simply add it like this

'assets': {
   'web.assets_backend': [
        'ateam_adaptations/static/src/views/inspector/documents_inspector.js'
   ]
}

I hope it is helpful

Upvotes: 1

Related Questions