Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions child_compassion/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,18 +43,18 @@ Configuration
Access rights
-------------

- Assign the rights to the users so that they can access the new
"Sponsorship" menu
- Assign the rights to the users so that they can access the new
"Sponsorship" menu

Configuration menu
------------------

- Find the configuration menu in Sponsorship/Configuration (must be
given rights)
- Configure the default hold durations in the menu Global
Childpool/Availability Management
- Assign people to be notified when receiving National Office Disaster
Alerts using the menu Communication/Staff Notifications
- Find the configuration menu in Sponsorship/Configuration (must be
given rights)
- Configure the default hold durations in the menu Global
Childpool/Availability Management
- Assign people to be notified when receiving National Office Disaster
Alerts using the menu Communication/Staff Notifications

Odoo.conf file
--------------
Expand All @@ -70,8 +70,8 @@ Demand planning
You can add in the system settings default values for weekly demand and
resupply quantities by setting the following keys:

- child_compassion.default_demand
- child_compassion.default_resupply
- child_compassion.default_demand
- child_compassion.default_resupply

Usage
=====
Expand All @@ -84,8 +84,8 @@ Changelog
14.0.1.4.0
----------

- ADD World Bank Data API fields in the Field Offices for fetching
country statistics.
- ADD World Bank Data API fields in the Field Offices for fetching
country statistics.

Bug Tracker
===========
Expand All @@ -108,10 +108,10 @@ Authors
Contributors
------------

- Emanuel Cino <ecino@compassion.ch>
- Cyril Sester <cyril.sester@outlook.com>
- Kevin Cristi <kcristi@compassion.ch>
- David Coninckx <david@coninckx.com>
- Emanuel Cino <ecino@compassion.ch>
- Cyril Sester <cyril.sester@outlook.com>
- Kevin Cristi <kcristi@compassion.ch>
- David Coninckx <david@coninckx.com>

Maintainers
-----------
Expand Down
40 changes: 22 additions & 18 deletions child_compassion/models/compassion_hold.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,24 +486,28 @@ def postpone_no_money_hold(self, additional_text=None):
}
hold.write(hold_vals)

body = (
"The no money hold for child {local_id} was expiring on "
"{old_expiration} and was extended to {new_expiration} "
"({extension_description} extension).{additional_text}"
)
hold.message_post(
body=_(body.format(**values)),
subject=_("No money hold extension"),
subtype_xmlid="mail.mt_comment",
)

else:
body = _(
"The no money hold for child {local_id} is expiring on "
"{old_expiration} and will not be extended since "
"no sponsorship exists for this child."
)
hold.message_post(body=body.format(**values))
# ----------------------------------------------------
# NOTIFICATION (Only for NO_MONEY_HOLD)
# ----------------------------------------------------
if hold.type == HoldType.NO_MONEY_HOLD.value:
if hold.child_id.sponsor_id:
body = (
"The no money hold for child {local_id} was expiring on "
"{old_expiration} and was extended to {new_expiration} "
"({extension_description} extension).{additional_text}"
)
hold.message_post(
body=_(body.format(**values)),
subject=_("No money hold extension"),
subtype_xmlid="mail.mt_comment",
)
else:
body = _(
"The no money hold for child {local_id} is expiring on "
"{old_expiration} and will not be extended since "
"no sponsorship exists for this child."
)
hold.message_post(body=body.format(**values))

##########################################################################
# Mapping METHOD #
Expand Down
2 changes: 1 addition & 1 deletion child_compassion/wizards/global_child_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ def do_search(self):

def add_search(self):
self.ensure_one()
self.skip += self.nb_selected
self.skip += self.take
if not self.advanced_criteria_used:
self._call_search_service(
"profile_search",
Expand Down
14 changes: 14 additions & 0 deletions partner_communication/models/communication_snippet.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
from odoo import fields, models


class CommunicationSnippetCategory(models.Model):
_name = "communication.snippet.category"
_description = "Communication Snippet Category"

name = fields.Char(string="Category Name", required=True)


class CommunicationSnippet(models.Model):
_name = "communication.snippet"
_description = "Communication Snippet"

name = fields.Char(required=True, index=True)
snippet_text = fields.Html(required=True, translate=True)
description = fields.Text(string="Description")

category_id = fields.Many2one(
"communication.snippet.category",
string="Category",
help="Category of the communication snippet",
)

def action_edit_snippet(self):
self.ensure_one()
Expand Down
1 change: 1 addition & 0 deletions partner_communication/security/ir.model.access.csv
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ access_partner_communication_generate_wizard,access_partner_communication_genera
access_partner_communication_download_print_wizard,access_partner_communication_download_print_wizard,model_partner_communication_download_print_job_wizard,base.group_user,1,0,1,0
access_partner_communication_default_config,access_partner_communication_default_config,model_partner_communication_default_config,base.group_user,1,1,1,1
access_communication_snippets,Full access on communication_snippets,model_communication_snippet,base.group_user,1,1,1,1
access_comm_snippet_category_user,communication.snippet.category,model_communication_snippet_category,base.group_user,1,1,1,1
10 changes: 10 additions & 0 deletions partner_communication/views/communication_snippet_view.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@
<group>
<field name="name" />
<field name="snippet_text" />
<field
name="category_id"
options="{'no_create_edit': False}"
/>
<field
name="description"
placeholder="Useful for global numbers..."
/>
</group>
</sheet>
</form>
Expand All @@ -20,6 +28,8 @@
<tree editable="top">
<field name="name" />
<field name="snippet_text" />
<field name="category_id" />
<field name="description" />
<button
name="action_edit_snippet"
type="object"
Expand Down
139 changes: 98 additions & 41 deletions sbc_compassion/models/correspondence_s2b_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,22 @@ class CorrespondenceS2bGenerator(models.Model):
preview_pdf = fields.Binary(readonly=True)
filename = fields.Char(compute="_compute_filename")
month = fields.Selection("_get_months")
generation_status = fields.Selection(
[
("creating_task", "creating_task"),
("apply_template", "apply_template"),
("apply_text", "apply_text"),
("apply_images", "apply_images"),
("generate_pdf", "generate_pdf"),
("done", "done"),
("failed", "failed"),
("finalizing", "finalizing"),
],
default="creating_task",
string="Generation Status",
)
generation_error_message = fields.Text(string="Generation Message")
MAX_PAGE_COUNT = 15 # Maximum number of pages allowed in a letter

def _compute_nb_letters(self):
for generator in self:
Expand Down Expand Up @@ -130,50 +146,63 @@ def onchange_month(self):

def preview(self):
"""Generate a picture for preview."""
pdf = self._get_pdf(self.sponsorship_ids[:1])[0]
if self.template_id.layout == "CH-A-3S01-1":
# Read page 2
in_pdf = PdfFileReader(BytesIO(pdf))
output_pdf = PdfFileWriter()
out_data = BytesIO()
output_pdf.addPage(in_pdf.getPage(1))
output_pdf.write(out_data)
out_data.seek(0)
pdf = out_data.read()

try:
pdf = self._get_pdf(self.sponsorship_ids[:1])[0]

if self.template_id.layout == "CH-A-3S01-1":
in_pdf = PdfFileReader(BytesIO(pdf))
output_pdf = PdfFileWriter()
output_pdf.addPage(in_pdf.getPage(1))
out_data = BytesIO()
output_pdf.write(out_data)
pdf = out_data.getvalue()

n_pages = PdfFileReader(BytesIO(pdf)).getNumPages()
if n_pages > self.MAX_PAGE_COUNT:
msg = _("Oops your letter has %d pages. The limit is %d.") % (
n_pages,
self.MAX_PAGE_COUNT,
)

raise UserError(msg)
with Image(blob=pdf, resolution=96) as pdf_image:
preview = base64.b64encode(pdf_image.make_blob(format="jpeg"))
except PolicyError as error:
_logger.error(
"ImageMagick policy error. Please add following line to "
"/etc/Image-Magick-<version>/policy.xml: "
'<policy domain="coder" rights="read|write" '
'pattern="PDF" />',
)
raise UserError(
_(
"Please allow ImageMagick to write PDF files."
" Ask an IT admin for help."

return self.isolated_write(
{
"state": "preview",
"generation_status": "done",
"generation_error_message": False,
"preview_image": preview,
"preview_pdf": base64.b64encode(pdf),
}
)
) from error
except TypeError as error:
raise UserError(

except (PolicyError, TypeError, UserError, Exception) as error:
error_message = (
_(
"Unfortunately the server cannot generate PDF documents "
"at the moment. Our IT team is informed and will fix this issue "
"as soon as possible."
)
if isinstance(error, PolicyError)
else str(error)
if isinstance(error, UserError)
else _(
"There was an error while generating the PDF of the letter. "
"Please check FPDF logs for more information."
)
) from error

pdf_image = base64.b64encode(pdf)

return self.write(
{
"state": "preview",
"preview_image": preview,
"preview_pdf": pdf_image,
}
)
)
Comment on lines +182 to +195
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The nested ternary operator for assigning error_message is a bit complex and can be hard to read. Refactoring this into a standard if/elif/else structure would improve code clarity and maintainability.

            if isinstance(error, PolicyError):
                error_message = _(
                    "Unfortunately the server cannot generate PDF documents "
                    "at the moment. Our IT team is informed and will fix this issue "
                    "as soon as possible."
                )
            elif isinstance(error, UserError):
                error_message = str(error)
            else:
                error_message = _(
                    "There was an error while generating the PDF of the letter. "
                    "Please check FPDF logs for more information."
                )

_logger.error("Unable to generate PDF", exc_info=True)
if self.env.context.get("raise_error"):
raise UserError(error_message) from error
self.env.cr.rollback()
self.isolated_write(
{
"generation_status": "failed",
"generation_error_message": error_message,
}
)

def edit(self):
"""Generate a picture for preview."""
Expand All @@ -184,6 +213,7 @@ def generate_letters(self):
Launch S2B Creation job
:return: True
"""
self.generation_status = "finalizing"
self.with_delay(
identity_key="s2b_generator." + str(self.ids)
).generate_letters_job()
Expand Down Expand Up @@ -217,7 +247,7 @@ def generate_letters_job(self):
"res_model": letters._name,
},
)
for atchmt in self.image_ids
for atchmt in self.image_ids.sorted(reverse=True)
]
letters += letters.create(vals)

Expand All @@ -227,11 +257,26 @@ def generate_letters_job(self):
# If the operation succeeds, notify the user
message = "Letters have been successfully generated."
self.env.user.notify_success(message=message)
return self.write({"state": "done", "date": fields.Datetime.now()})

return self.isolated_write(
{
"state": "done",
"date": fields.Datetime.now(),
"generation_status": "done",
"generation_error_message": False,
}
)

except Exception as error:
# If the operation fails, notify the user with the error message
self.env.cr.rollback()
error_message = str(error)
self.isolated_write(
{
"generation_status": "failed",
"generation_error_message": error_message,
}
)
_logger.error(error_message, exc_info=True)
self.env.user.notify_danger(message=error_message)

Expand All @@ -257,8 +302,8 @@ def _get_text(self, sponsorship):
keywords = {
"%child%": child.preferred_name,
"%age%": str(child.age),
"%firstname%": sponsor.firstname or sponsor.name,
"%lastname%": sponsor.firstname and sponsor.lastname or "",
"%firstname%": sponsor.preferred_name or sponsor.firstname or sponsor.name,
"%lastname%": sponsor.lastname or "",
}
text = self.body
for keyword, replacement in list(keywords.items()):
Expand All @@ -285,7 +330,19 @@ def _get_pdf(self, sponsorship):
sponsorship.display_name,
(header, ""), # Headers (front/back)
{"Original": [text]}, # Text
self.mapped("image_ids.datas"), # Images
self.mapped("image_ids").sorted(reverse=True).mapped("datas"), # Images
s2b_generator=self,
),
text,
)

def isolated_write(self, vals):
"""Use a separate transaction to update the letter_generator."""
if len(self) != 1:
return False

with self.env.registry.cursor() as new_cr:
new_env = self.env(cr=new_cr)
new_s2b_generator = new_env[self._name].browse(self.id)
new_s2b_generator.write(vals)
new_cr.commit()
Loading