first commit

This commit is contained in:
admin.suherdy 2025-12-17 14:30:04 +07:00
commit e7c8c09ca3
10 changed files with 158 additions and 0 deletions

3
__init__.py Normal file
View File

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import models

33
__manifest__.py Normal file
View File

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Sign Image Field',
'version': '1.0',
'category': 'Document Management',
'summary': 'Add Image field to Sign module',
'description': """
This module extends the Odoo Sign module to allow users to upload images as a field type.
""",
'depends': ['sign'],
'data': [
'data/sign_item_type_data.xml',
],
'assets': {
'web.assets_frontend': [
'sign_image_field/static/src/js/sign_image_upload.js',
'sign_image_field/static/src/xml/sign_items_image.xml',
],
'web.assets_backend': [
'sign_image_field/static/src/js/sign_image_upload.js',
'sign_image_field/static/src/xml/sign_items_image.xml',
],
'sign.assets_public_sign': [
'sign_image_field/static/src/js/sign_image_upload.js',
'sign_image_field/static/src/xml/sign_items_image.xml',
]
},
'installable': True,
'application': False,
'license': 'OEEL-1',
}

Binary file not shown.

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="sign_item_type_image" model="sign.item.type">
<field name="name">Image</field>
<field name="item_type">image</field>
<field name="icon">fa-image</field>
<field name="tip">Upload an image</field>
<field name="placeholder">Click to upload image</field>
<field name="default_width">0.2</field>
<field name="default_height">0.05</field>
</record>
</odoo>

3
models/__init__.py Normal file
View File

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import sign_item_type

Binary file not shown.

Binary file not shown.

10
models/sign_item_type.py Normal file
View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models
class SignItemType(models.Model):
_inherit = "sign.item.type"
item_type = fields.Selection(selection_add=[('image', "Image")], ondelete={'image': 'cascade'})

View File

@ -0,0 +1,59 @@
/** @odoo-module **/
import { patch } from "@web/core/utils/patch";
import { SignablePDFIframe } from "@sign/components/sign_request/signable_PDF_iframe";
patch(SignablePDFIframe.prototype, {
enableCustom(signItem) {
super.enableCustom(signItem);
if (signItem.data.type === 'image') {
const input = signItem.el.querySelector('.o_sign_image_upload_input');
if (!input) return;
signItem.el.addEventListener('click', (e) => {
if (this.readonly || signItem.data.responsible !== this.currentRole) return;
// Prevent recursive click if clicking the input itself bubbles up
if (e.target !== input) {
input.click();
}
});
input.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = () => {
const result = reader.result;
signItem.el.dataset.value = result;
// Manually update the DOM to show the image
const img = signItem.el.querySelector('img');
if (img) {
img.src = result;
} else {
// Remove placeholder if it exists
const placeholder = signItem.el.querySelector('.o_placeholder');
if (placeholder) placeholder.remove();
const newImg = document.createElement('img');
newImg.src = result;
newImg.style.maxWidth = '100%';
newImg.style.maxHeight = '100%';
newImg.style.objectFit = 'contain';
signItem.el.appendChild(newImg);
}
this.handleInput(); // Trigger validation check
};
reader.readAsDataURL(file);
});
}
},
getSignatureValueFromElement(item) {
if (item.data.type === 'image') {
return item.el.dataset.value || false;
}
return super.getSignatureValueFromElement(item);
}
});

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="sign.signItem" t-inherit="sign.signItem" t-inherit-mode="extension">
<xpath expr="//t[@t-if=&quot;type == 'selection'&quot;]" position="after">
<t t-if="type == 'image'" t-call="sign.imageSignItem"/>
</xpath>
<xpath expr="//div[@t-if=&quot;type == 'selection'&quot;]" position="after">
<div t-if="type == 'image'" t-att-title="role" t-attf-class="{{classes}} o_sign_sign_item" t-att-style="style" t-att-data-value="value" style="text-align:center;">
<input type="file" accept="image/*" class="o_sign_image_upload_input" style="display:none"/>
<t t-if="value">
<img t-att-src="value" style="max-width:100%; max-height:100%; object-fit:contain;"/>
</t>
<t t-else="">
<span class="o_placeholder">
<t t-esc="placeholder"/>
</span>
</t>
<t t-if="isSignItemEditable" t-call="sign.signItemConfiguration"/>
</div>
</xpath>
</t>
<t t-name="sign.imageSignItem">
<div t-att-title="role" t-attf-class="{{classes}} o_sign_sign_item" t-att-data-id="id" t-att-style="style">
<div class="sign_item_body" style="text-align:center;">
<t t-if="value">
<img t-att-src="value" style="max-width:100%; max-height:100%; object-fit:contain;"/>
</t>
<t t-else="">
<span class="o_placeholder">
<t t-esc="placeholder"/>
</span>
</t>
<t t-if="editMode || isSignItemEditable" t-call="sign.signItemConfiguration"/>
</div>
</div>
</t>
</templates>