From 5fb2bfab4b991ab812d07da315a4c0e422b6049a Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 12 Apr 2022 13:22:32 +0530 Subject: [PATCH 01/45] feat: allow multiple client scripts --- .../doctype/client_script/client_script.json | 5 ++++- .../custom/doctype/client_script/client_script.py | 14 -------------- frappe/desk/form/meta.py | 14 +++++++++++--- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/frappe/custom/doctype/client_script/client_script.json b/frappe/custom/doctype/client_script/client_script.json index eca84b4dec..1db4dfe160 100644 --- a/frappe/custom/doctype/client_script/client_script.json +++ b/frappe/custom/doctype/client_script/client_script.json @@ -1,6 +1,7 @@ { "actions": [], "allow_import": 1, + "autoname": "Prompt", "creation": "2013-01-10 16:34:01", "description": "Adds a custom client script to a DocType", "doctype": "DocType", @@ -52,6 +53,7 @@ "default": "Form", "fieldname": "view", "fieldtype": "Select", + "in_list_view": 1, "label": "Apply To", "options": "List\nForm", "set_only_once": 1 @@ -75,10 +77,11 @@ "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2022-02-18 00:43:33.941466", + "modified": "2022-04-12 12:48:15.717985", "modified_by": "Administrator", "module": "Custom", "name": "Client Script", + "naming_rule": "Set by user", "owner": "Administrator", "permissions": [ { diff --git a/frappe/custom/doctype/client_script/client_script.py b/frappe/custom/doctype/client_script/client_script.py index 3039e0a4a5..b60f5708d1 100644 --- a/frappe/custom/doctype/client_script/client_script.py +++ b/frappe/custom/doctype/client_script/client_script.py @@ -6,20 +6,6 @@ from frappe.model.document import Document class ClientScript(Document): - def autoname(self): - self.name = f"{self.dt}-{self.view}" - - def validate(self): - if not self.is_new(): - return - - exists = frappe.db.exists("Client Script", {"dt": self.dt, "view": self.view}) - if exists: - frappe.throw( - _("Client Script for {0} {1} already exists").format(frappe.bold(self.dt), self.view), - frappe.DuplicateEntryError, - ) - def on_update(self): frappe.clear_cache(doctype=self.dt) diff --git a/frappe/desk/form/meta.py b/frappe/desk/form/meta.py index ba19377c48..f5edd3fcc6 100644 --- a/frappe/desk/form/meta.py +++ b/frappe/desk/form/meta.py @@ -155,7 +155,7 @@ class FormMeta(Meta): frappe.db.get_all( "Client Script", filters={"dt": self.name, "enabled": 1}, - fields=["script", "view"], + fields=["name", "script", "view"], order_by="creation asc", ) or "" @@ -165,10 +165,18 @@ class FormMeta(Meta): form_script = "" for script in client_scripts: if script.view == "List": - list_script += script.script + list_script += f""" +// {script.name} +{script.script} + +""" if script.view == "Form": - form_script += script.script + form_script += f""" +// {script.name} +{script.script} + +""" file = scrub(self.name) form_script += f"\n\n//# sourceURL={file}__custom_js" From d94d30daccb50056e907d4079ebc225a8faca22e Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 13 Apr 2022 18:36:44 +0530 Subject: [PATCH 02/45] test: set name to create client script --- frappe/tests/test_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/tests/test_utils.py b/frappe/tests/test_utils.py index a56d404fac..04f9d16fd1 100644 --- a/frappe/tests/test_utils.py +++ b/frappe/tests/test_utils.py @@ -354,8 +354,8 @@ class TestPythonExpressions(unittest.TestCase): class TestDiffUtils(unittest.TestCase): @classmethod def setUpClass(cls): - cls.doc = frappe.get_doc(doctype="Client Script", dt="Client Script") - cls.doc.save(ignore_version=False) + cls.doc = frappe.get_doc(doctype="Client Script", dt="Client Script", name="test_client_script") + cls.doc.insert() cls.doc.script = "2;" cls.doc.save(ignore_version=False) cls.doc.script = "42;" From 32e4f932bba133ef107deaf5df5fc8b658551b76 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 14 Apr 2022 00:17:26 +0530 Subject: [PATCH 03/45] test: ui test for client scripts --- .../client_script/ui_test_client_script.js | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 frappe/custom/doctype/client_script/ui_test_client_script.js diff --git a/frappe/custom/doctype/client_script/ui_test_client_script.js b/frappe/custom/doctype/client_script/ui_test_client_script.js new file mode 100644 index 0000000000..022f677151 --- /dev/null +++ b/frappe/custom/doctype/client_script/ui_test_client_script.js @@ -0,0 +1,101 @@ +context("Client Script", () => { + before(() => { + cy.login(); + cy.visit("/app"); + }); + + it("should run form script in doctype form", () => { + cy.insert_doc( + "Client Script", + { + name: "Todo form script", + dt: "ToDo", + view: "Form", + enabled: 1, + script: `console.log('todo form script')` + }, + true + ); + cy.visit("/app/todo/new", { + onBeforeLoad(win) { + cy.spy(win.console, "log").as("consoleLog"); + } + }); + cy.get("@consoleLog").should("be.calledWith", "todo form script"); + }); + + it("should run list script in doctype list view", () => { + cy.insert_doc( + "Client Script", + { + name: "Todo list script", + dt: "ToDo", + view: "List", + enabled: 1, + script: `console.log('todo list script')` + }, + true + ); + cy.visit("/app/todo", { + onBeforeLoad(win) { + cy.spy(win.console, "log").as("consoleLog"); + } + }); + cy.get("@consoleLog").should("be.calledWith", "todo list script"); + }); + + it("should not run disabled scripts", () => { + cy.insert_doc( + "Client Script", + { + name: "Todo disabled list", + dt: "ToDo", + view: "List", + enabled: 0, + script: `console.log('todo disabled script')` + }, + true + ); + cy.visit("/app/todo", { + onBeforeLoad(win) { + cy.spy(win.console, "log").as("consoleLog"); + } + }); + cy.get("@consoleLog").should( + "not.be.calledWith", + "todo disabled script" + ); + }); + + it("should run multiple scripts", () => { + cy.insert_doc( + "Client Script", + { + name: "Todo form script 1", + dt: "ToDo", + view: "Form", + enabled: 1, + script: `console.log('todo form script 1')` + }, + true + ); + cy.insert_doc( + "Client Script", + { + name: "Todo form script 2", + dt: "ToDo", + view: "Form", + enabled: 1, + script: `console.log('todo form script 2')` + }, + true + ); + cy.visit("/app/todo/new", { + onBeforeLoad(win) { + cy.spy(win.console, "log").as("consoleLog"); + } + }); + cy.get("@consoleLog").should("be.calledWith", "todo form script 1"); + cy.get("@consoleLog").should("be.calledWith", "todo form script 2"); + }); +}); From b409a7eeba7f1ad249ec1697ec4fae70def44152 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 14 Apr 2022 15:16:59 +0530 Subject: [PATCH 04/45] fix: extend autoname validation to child items --- frappe/model/base_document.py | 2 +- frappe/model/document.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 4f2ddd3bb6..02eb2ab38c 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -869,7 +869,7 @@ class BaseDocument(object): autoname = self.meta.autoname or "" _empty, _field_specifier, fieldname = autoname.partition("field:") - if fieldname and self.name and self.name != self.get("fieldname"): + if fieldname and self.name and self.name != self.get(fieldname): self.set(fieldname, self.name) def throw_length_exceeded_error(self, df, max_length, value): diff --git a/frappe/model/document.py b/frappe/model/document.py index 07ea58d8e9..58c406607d 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -528,6 +528,7 @@ class Document(BaseDocument): d._validate_non_negative() d._validate_length() d._validate_code_fields() + d._sync_autoname_field() d._extract_images_from_text_editor() d._sanitize_content() d._save_passwords() From a615c60be44efe5d49fbdd4df37d87366b14ab09 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 14 Apr 2022 16:33:23 +0530 Subject: [PATCH 05/45] test: naming on child tables --- frappe/tests/test_naming.py | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/frappe/tests/test_naming.py b/frappe/tests/test_naming.py index 33974e5d27..e217f24154 100644 --- a/frappe/tests/test_naming.py +++ b/frappe/tests/test_naming.py @@ -4,11 +4,11 @@ import unittest import frappe +from frappe.core.doctype.doctype.test_doctype import new_doctype from frappe.model.naming import ( append_number_if_name_exists, determine_consecutive_week_number, getseries, - parse_naming_series, revert_series_if_last, ) from frappe.utils import now_datetime @@ -51,6 +51,40 @@ class TestNaming(unittest.TestCase): self.assertEqual(country.name, original_name) self.assertEqual(country.name, country.country_name) + def test_child_table_naming(self): + child_dt_with_naming = new_doctype( + "childtable_with_autonaming", istable=1, autoname="field:some_fieldname" + ).insert() + dt_with_child_autoname = new_doctype( + "dt_with_childtable_naming", + fields=[ + { + "label": "table with naming", + "fieldname": "table_with_naming", + "options": "childtable_with_autonaming", + "fieldtype": "Table", + } + ], + ).insert() + + name = frappe.generate_hash(length=10) + + doc = frappe.new_doc("dt_with_childtable_naming") + doc.append("table_with_naming", {"some_fieldname": name}) + doc.save() + self.assertEqual(doc.table_with_naming[0].name, name) + + # change autoname field + doc.table_with_naming[0].some_fieldname = "Something else" + doc.save() + + self.assertEqual(doc.table_with_naming[0].name, name) + self.assertEqual(doc.table_with_naming[0].some_fieldname, name) + + doc.delete() + dt_with_child_autoname.delete() + child_dt_with_naming.delete() + def test_format_autoname(self): """ Test if braced params are replaced in format autoname From 2af2b80ba62c50e325d6500eedf7a9c2a5ee356b Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 14 Apr 2022 18:59:39 +0530 Subject: [PATCH 06/45] feat: Expose add index in exec_globals (#16605) * feat: Expose add index in exec_globals * fix: Linting issues --- .../server_script/test_server_script.py | 22 +++++++++++++++++++ frappe/utils/safe_exec.py | 3 +++ 2 files changed, 25 insertions(+) diff --git a/frappe/core/doctype/server_script/test_server_script.py b/frappe/core/doctype/server_script/test_server_script.py index 2685367695..fd600b8205 100644 --- a/frappe/core/doctype/server_script/test_server_script.py +++ b/frappe/core/doctype/server_script/test_server_script.py @@ -72,6 +72,16 @@ frappe.method_that_doesnt_exist("do some magic") disabled=1, script=""" frappe.db.commit() +""", + ), + dict( + name="test_add_index", + script_type="DocType Event", + doctype_event="Before Save", + reference_doctype="ToDo", + disabled=1, + script=""" +frappe.db.add_index("Todo", ["color", "date"]) """, ), ] @@ -153,6 +163,18 @@ class TestServerScript(unittest.TestCase): server_script.disabled = 1 server_script.save() + def test_add_index_in_doctype_event(self): + server_script = frappe.get_doc("Server Script", "test_add_index") + server_script.disabled = 0 + server_script.save() + + self.assertRaises( + AttributeError, frappe.get_doc(dict(doctype="ToDo", description="test me")).insert + ) + + server_script.disabled = 1 + server_script.save() + def test_restricted_qb(self): todo = frappe.get_doc(doctype="ToDo", description="QbScriptTestNote") todo.insert() diff --git a/frappe/utils/safe_exec.py b/frappe/utils/safe_exec.py index 0a775fafec..aa6fa8b67f 100644 --- a/frappe/utils/safe_exec.py +++ b/frappe/utils/safe_exec.py @@ -56,8 +56,10 @@ def safe_exec(script, _globals=None, _locals=None, restrict_commit_rollback=Fals exec_globals.update(_globals) if restrict_commit_rollback: + # prevent user from using these in docevents exec_globals.frappe.db.pop("commit", None) exec_globals.frappe.db.pop("rollback", None) + exec_globals.frappe.db.pop("add_index", None) # execute script compiled by RestrictedPython frappe.flags.in_safe_exec = True @@ -155,6 +157,7 @@ def get_safe_globals(): sql=read_sql, commit=frappe.db.commit, rollback=frappe.db.rollback, + add_index=frappe.db.add_index, ), ), FrappeClient=FrappeClient, From 0ae7248847399e5ef3499afea943398d1c715e38 Mon Sep 17 00:00:00 2001 From: HENRY Florian Date: Thu, 14 Apr 2022 19:13:57 +0200 Subject: [PATCH 07/45] fix: update translation (#16623) --- frappe/translations/fr.csv | 54 ++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/frappe/translations/fr.csv b/frappe/translations/fr.csv index 67aca1443c..be0d6a6892 100644 --- a/frappe/translations/fr.csv +++ b/frappe/translations/fr.csv @@ -418,8 +418,8 @@ Also adding the dependent currency field {0},Ajout également du champ de devise "Always add ""Draft"" Heading for printing draft documents","Toujours ajouter ""Brouillon"" pour l'impression des documents brouillons", Always use Account's Email Address as Sender,Toujours utiliser l'adresse Email du compte comme Expéditeur, Always use Account's Name as Sender's Name,Toujours utiliser le nom du compte comme nom de l'expéditeur, -Amend,Modifier, -Amending,Modification, +Amend,Nouv. version +Amending,Nouv. version en cours, Amount Based On Field,Montant Basé sur le Champ, Amount Field,Champ du Montant, Amount must be greater than 0.,Le montant doit être supérieur à 0., @@ -1100,7 +1100,7 @@ File not attached,Fichier joint manquant, File size exceeded the maximum allowed size of {0} MB,La taille du fichier a dépassé la taille maximale autorisée de {0} Mo, File too big,Fichier trop grand, File {0} does not exist,Fichier {0} n'existe pas, -Files,Des dossiers, +Files,Fichiers, Filter,Filtre, Filter Data,Filtrer les Données, Filter List,Liste de filtres, @@ -1145,7 +1145,7 @@ For Document Type,Pour le type de document, For User,Pour l\'Utilisateur, For Value,Pour la Valeur, "For currency {0}, the minimum transaction amount should be {1}","Pour la devise {0}, le montant minimum de la transaction doit être {1}", -For example if you cancel and amend INV004 it will become a new document INV004-1. This helps you to keep track of each amendment.,"Par exemple, si vous annulez et modifiez le document INV004, il deviendra un nouveau document INV004-1. Cela vous aide à garder une trace de chaque modification.", +For example if you cancel and amend INV004 it will become a new document INV004-1. This helps you to keep track of each amendment.,"Par exemple, si vous annulez et creer une nouvelle version le document INV004, il deviendra un nouveau document INV004-1. Cela vous aide à garder une trace de chaque modification.", "For example: If you want to include the document ID, use {0}","Par exemple: Si vous voulez inclure l'ID du document, utilisez {0}", "For updating, you can update only selective columns.","Pour la mise à jour, vous pouvez mettre à jour uniquement une sélection colonnes.", For {0} at level {1} in {2} in row {3},Pour {0} au niveau {1} dans {2} à la ligne {3}, @@ -1541,7 +1541,7 @@ Max Value,Valeur Max, Max width for type Currency is 100px in row {0},Largeur max pour le type Devise est 100px dans la ligne {0}, Maximum Attachment Limit for this record reached.,Taille maximale des Pièces Jointes pour cet enregistrement est atteint., Maximum {0} rows allowed,Maximum {0} lignes autorisés, -"Meaning of Submit, Cancel, Amend","Signification de Valider, Annuler, Modifier", +"Meaning of Submit, Cancel, Amend","Signification de Valider, Annuler, Nouv. version", Mention transaction completion page URL,Mentionnez la page URL de fin de transaction, Mentions,Mentions, Menu,Menu, @@ -1737,7 +1737,7 @@ Old Password,Ancien Mot De Passe, Old Password Required.,Ancien Mot de Passe Requis., Older backups will be automatically deleted,Les anciennes sauvegardes seront automatiquement supprimées, "On {0}, {1} wrote:","Sur {0}, {1} a écrit :", -"Once submitted, submittable documents cannot be changed. They can only be Cancelled and Amended.","Une fois validé, les documents à valider ne peuvent plus être modifiés. Ils ne peuvent être annulés et amendés.", +"Once submitted, submittable documents cannot be changed. They can only be Cancelled and Amended.","Une fois validé, les documents à valider ne peuvent plus être modifiés. Ils ne peuvent être annulés et modifiés (Nouv. version).", "Once you have set this, the users will only be able access documents (eg. Blog Post) where the link exists (eg. Blogger).","Une fois que vous avez défini ceci, les utilisateurs ne pourront accèder qu'aux documents (e.g. Article de Blog) où le lien existe (e.g. Blogger) .", One Last Step,Une Dernière Étape, One Time Password (OTP) Registration Code from {},Code de Mot de Passe Unique (OTP) à partir de {}, @@ -1837,7 +1837,7 @@ Permission Levels,Niveaux d'Autorisation, Permission Rules,Règles d'Autorisation, Permissions,Autorisations, Permissions are automatically applied to Standard Reports and searches.,Les autorisations sont automatiquement appliquées aux rapports standard et aux recherches., -"Permissions are set on Roles and Document Types (called DocTypes) by setting rights like Read, Write, Create, Delete, Submit, Cancel, Amend, Report, Import, Export, Print, Email and Set User Permissions.","Les Autorisations sont définies sur les Rôles et les Types de Documents (appelés DocTypes) en définissant des droits , tels que Lire, Écrire, Créer, Supprimer, Valider, Annuler, Modifier, Rapporter, Importer, Exporter, Imprimer, Envoyer un Email et Définir les Autorisations de l'Utilisateur .", +"Permissions are set on Roles and Document Types (called DocTypes) by setting rights like Read, Write, Create, Delete, Submit, Cancel, Amend, Report, Import, Export, Print, Email and Set User Permissions.","Les Autorisations sont définies sur les Rôles et les Types de Documents (appelés DocTypes) en définissant des droits , tels que Lire, Écrire, Créer, Supprimer, Valider, Annuler, Nouv. version, Rapporter, Importer, Exporter, Imprimer, Envoyer un Email et Définir les Autorisations de l'Utilisateur .", Permissions at higher levels are Field Level permissions. All Fields have a Permission Level set against them and the rules defined at that permissions apply to the field. This is useful in case you want to hide or make certain field read-only for certain Roles.,Les Autorisations aux niveaux supérieurs sont des permissions de Niveau Champ. Un Niveau d'Autorisation est défini pour chaque Champ et les règles définies pour ces Autorisations s’appliquent au Champ. Ceci est utile si vous voulez cacher ou mettre certains champs en lecture seule pour certains Rôles., "Permissions at level 0 are Document Level permissions, i.e. they are primary for access to the document.","Les Autorisations au niveau 0 sont les autorisations de Niveau Document, c’est à dire qu'elles sont nécessaires pour accéder au document.", Permissions get applied on Users based on what Roles they are assigned.,Autorisations sont appliqués aux utilisateurs en fonction des Rôles qui leurs sont affectés., @@ -2745,7 +2745,7 @@ Website Theme Image,Image du Thème du Site Web, Website Theme Image Link,Lien de l'Image du Thème du Site Web, Website User,Utilisateur du Site Web, Welcome Message,Message de Bienvenue, -"When you Amend a document after Cancel and save it, it will get a new number that is a version of the old number.","Lorsque vous Modifiez un document après l'avoir Annulé et que vous l'enregistrez, il va obtenir un nouveau numéro qui est une version de l'ancien numéro.", +"When you Amend a document after Cancel and save it, it will get a new number that is a version of the old number.","Lorsque vous faite une nouvelle version d'un document après l'avoir Annulé et que vous l'enregistrez, il va obtenir un nouveau numéro qui est une version de l'ancien numéro.", Width,Largeur, Widths can be set in px or %.,Les largeurs peuvent être réglées en px ou %., Will be used in url (usually first name).,Sera utilisé dans l'url (généralement le prénom)., @@ -2784,7 +2784,7 @@ You are not permitted to view the newsletter.,Vous n'êtes pas autorisé à You are now following this document. You will receive daily updates via email. You can change this in User Settings.,Vous suivez maintenant ce document. Vous recevrez des mises à jour quotidiennes par courrier électronique. Vous pouvez modifier cela dans les paramètres de l'utilisateur., You can add dynamic properties from the document by using Jinja templating.,Vous pouvez ajouter des propriétés dynamiques au document à l'aide des modèles Jinja., You can also copy-paste this ,Vous pouvez également copier-coller cette, -"You can change Submitted documents by cancelling them and then, amending them.","Vous pouvez modifier les documents Validés en les annulant et ensuite, en les modifiant.", +"You can change Submitted documents by cancelling them and then, amending them.","Vous pouvez modifier les documents Validés en les annulant et ensuite, en créant une nouvelle version.", You can find things by asking 'find orange in customers',Vous pouvez trouver des choses en demandant 'trouver orange dans clients', You can only upload upto 5000 records in one go. (may be less in some cases),Vous pouvez seulement charger jusqu'à 5000 enregistrement en une seule fois. (peut-être moins dans certains cas), You can use Customize Form to set levels on fields.,Vous pouvez utiliser Personaliser le Formulaire pour définir les niveaux de champs., @@ -3093,12 +3093,12 @@ zoom-out,Réduire, {0} {1} to {2},{0} {1} à {2}, "{0}, Row {1}","{0}, Ligne {1}", "{0}: '{1}' ({3}) will get truncated, as max characters allowed is {2}",{0} : {1} '({3}) sera tronqué car le nombre de caractères max est {2}, -{0}: Cannot set Amend without Cancel,{0} : Impossible de choisir Modifier sans Annuler, -{0}: Cannot set Assign Amend if not Submittable,{0} : Impossible de définir ‘Assigner Modifier’ si non Validable, +{0}: Cannot set Amend without Cancel,{0} : Impossible de choisir Nouv. version sans Annuler, +{0}: Cannot set Assign Amend if not Submittable,{0} : Impossible de définir ‘Assigner Nouv. version’ si non Validable, {0}: Cannot set Assign Submit if not Submittable,{0} : Impossible de définir ‘Assigner Valider’ si non Validable, {0}: Cannot set Cancel without Submit,{0} : Impossible de choisir Annuler sans Valider, {0}: Cannot set Import without Create,{0} : Impossible de choisir Import sans Créer, -"{0}: Cannot set Submit, Cancel, Amend without Write","{0} : Vous ne pouvez pas choisir Valider, Annuler, Modifier sans Écrire", +"{0}: Cannot set Submit, Cancel, Amend without Write","{0} : Vous ne pouvez pas choisir Valider, Annuler, Nouv. version sans Écrire", {0}: Cannot set import as {1} is not importable,{0} : Impossible de choisir import car {1} n'est pas importable, {0}: No basic permissions set,{0} : Aucune autorisation de base définie, "{0}: Only one rule allowed with the same Role, Level and {1}","{0} : Une seule règle est permise avec le même Rôle, Niveau et {1}", @@ -3132,7 +3132,7 @@ No values to show,Aucune valeur à afficher, View Ref,Voir la référence, Workflow Action is not created for optional states,L'action de flux de travail n'est pas créée pour les états facultatifs, {0} values selected,{0} valeurs sélectionnées, -"""amended_from"" field must be present to do an amendment.",Le champ "modified_from" doit être présent pour effectuer un amendement., +"""amended_from"" field must be present to do an amendment.",Le champ "modified_from" doit être présent pour effectuer une Nouv. version, (Mandatory),(Obligatoire), 1 Google Calendar Event synced.,1 événement Google Agenda synchronisé., 1 record will be exported,1 enregistrement sera exporté, @@ -4139,6 +4139,7 @@ by Role,par rôle, Document is only editable by users with role,Le document n'est modifiable que par les utilisateurs avec un rôle, {0}: Other permission rules may also apply,{0}: d'autres règles d'autorisation peuvent également s'appliquer, {0} Page Views,{0} pages vues, +{0} Page Views,{0} pages vues, Expand,Développer, Collapse,Réduire, "Invalid Bearer token, please provide a valid access token with prefix 'Bearer'.","Jeton de porteur non valide, veuillez fournir un jeton d'accès valide avec le préfixe «porteur».", @@ -4530,7 +4531,7 @@ Delete and Generate New,Supprimer et générer un nouveau, {0} ({1}) (1 row mandatory),{0} ({1}) (1 ligne obligatoire), Select Fields To Insert,Sélectionnez les champs à insérer, Select Fields To Update,Sélectionnez les champs à mettre à jour, -"This document is already amended, you cannot ammend it again","Ce document est déjà modifié, vous ne pouvez plus le modifier", +"This document is already amended, you cannot ammend it again","Ce document est déjà obsoléte (une nouvelle version existe), vous ne pouvez plus le modifier", Add to ToDo,Ajouter à ToDo, {0} is currently {1},{0} est actuellement {1}, {0} are currently {1},{0} sont actuellement {1}, @@ -4702,16 +4703,17 @@ Negative Value,Valeur négative, Authentication failed while receiving emails from Email Account: {0}.,L'authentification a échoué lors de la réception des e-mails du compte de messagerie: {0}., Message from server: {0},Message du serveur: {0}, {0} edited this {1},{0} a édité {1}, -{0} created this {1}, {0} a créé {1} -Report an Issue, Signaler une anomalie -About, A Propos -My Profile, Mon profil -My Settings, Mes paramètres -Toggle Full Width, Changer l'affichage en pleine largeur -Toggle Theme, Basculer le thème -Theme Changed, Thème changé -Amend, Nouv. version -Document has been submitted, Document validé -Document has been cancelled, Document annulé -Document is in draft state, Document au statut brouillon +{0} created this {1},{0} a créé {1} +Report an Issue,Signaler une anomalie +User Forum,Forum utilisateur +About,A Propos +My Profile,Mon profil +My Settings,Mes paramètres +Toggle Full Width,Changer l'affichage en pleine largeur +Toggle Theme,Basculer le thème +Theme Changed,Thème changé +Document has been submitted,Document validé +Document has been cancelled,Document annulé +Document is in draft state,Document au statut brouillon Copy to Clipboard,Copier vers le presse-papiers +Don't have an account?,Vous n'avez pas de compte? From 3c1175ea15e6efc545431555feaf83c4481fb64a Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 15 Apr 2022 18:41:07 +0530 Subject: [PATCH 08/45] fix: Directly pass `link_title_doctypes` instead of calling it To avoid permission error --- frappe/website/doctype/web_form/templates/web_form.html | 2 +- frappe/website/doctype/web_form/web_form.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/website/doctype/web_form/templates/web_form.html b/frappe/website/doctype/web_form/templates/web_form.html index fff0598be3..2a02aa5f2b 100644 --- a/frappe/website/doctype/web_form/templates/web_form.html +++ b/frappe/website/doctype/web_form/templates/web_form.html @@ -85,7 +85,7 @@ frappe.boot = { system: "{{ frappe.utils.get_time_zone() }}", user: "{{ frappe.db.get_value('User', frappe.session.user, 'time_zone') or frappe.utils.get_time_zone() }}" }, - link_title_doctypes: `{{ frappe.call('frappe.boot.get_link_title_doctypes') }}` + link_title_doctypes: {{ link_title_doctypes }} }; // for backward compatibility of some libs frappe.sys_defaults = frappe.boot.sysdefaults; diff --git a/frappe/website/doctype/web_form/web_form.py b/frappe/website/doctype/web_form/web_form.py index 5740533ecc..5304fbdfab 100644 --- a/frappe/website/doctype/web_form/web_form.py +++ b/frappe/website/doctype/web_form/web_form.py @@ -198,6 +198,7 @@ def get_context(context): context.show_in_grid = self.show_in_grid self.load_translations(context) + context.link_title_doctypes = frappe.boot.get_link_title_doctypes() def load_translations(self, context): translated_messages = frappe.translate.get_dict("doctype", self.doc_type) From 7233c257c7e99f86ebe9f0cb3c2c335dc154fc5a Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sat, 16 Apr 2022 05:45:15 +0530 Subject: [PATCH 09/45] fix: handle filters passed as dict to client.get --- frappe/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/client.py b/frappe/client.py index e970a64802..a8223cdeee 100644 --- a/frappe/client.py +++ b/frappe/client.py @@ -75,7 +75,7 @@ def get(doctype, name=None, filters=None, parent=None): check_parent_permission(parent, doctype) if filters and not name: - name = frappe.db.get_value(doctype, json.loads(filters)) + name = frappe.db.get_value(doctype, frappe.parse_json(filters)) if not name: frappe.throw(_("No document found for given filters")) From 112da07451ad956d34337a6194163cdfd3a16e38 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sat, 16 Apr 2022 06:03:40 +0530 Subject: [PATCH 10/45] test: dict and JSON filters in client.get --- frappe/tests/test_client.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/frappe/tests/test_client.py b/frappe/tests/test_client.py index 1bb2867665..c86c482651 100644 --- a/frappe/tests/test_client.py +++ b/frappe/tests/test_client.py @@ -129,3 +129,15 @@ class TestClient(unittest.TestCase): self.assertTrue("name" in first_item) self.assertTrue("modified" in first_item) frappe.local.login_manager.logout() + + def test_client_get(self): + from frappe.client import get + + todo = frappe.get_doc(doctype="ToDo", description="test").insert() + filters = {"name": todo.name} + filters_json = frappe.as_json(filters) + + self.assertEqual(get("ToDo", filters=filters).description, "test") + self.assertEqual(get("ToDo", filters=filters_json).description, "test") + + todo.delete() From 839eee7bd53626b549ca1717c13fc502829a332b Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Sat, 16 Apr 2022 08:04:07 +0530 Subject: [PATCH 11/45] fix: current job was not shown in workers view because of `if` conditions that should only be checked when view is Jobs --- .../page/background_jobs/background_jobs.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/frappe/core/page/background_jobs/background_jobs.py b/frappe/core/page/background_jobs/background_jobs.py index d3f5e3d32f..09213d64c3 100644 --- a/frappe/core/page/background_jobs/background_jobs.py +++ b/frappe/core/page/background_jobs/background_jobs.py @@ -22,11 +22,7 @@ JOB_COLORS = {"queued": "orange", "failed": "red", "started": "blue", "finished" def get_info(view=None, queue_timeout=None, job_status=None) -> List[Dict]: jobs = [] - def add_job(job: "Job", name: str) -> None: - if job_status != "all" and job.get_status() != job_status: - return - if queue_timeout != "all" and not name.endswith(f":{queue_timeout}"): - return + def add_job(job: "Job", queue: str) -> None: if job.kwargs.get("site") == frappe.local.site: job_info = { @@ -34,7 +30,7 @@ def get_info(view=None, queue_timeout=None, job_status=None) -> List[Dict]: or job.kwargs.get("kwargs", {}).get("job_type") or str(job.kwargs.get("job_name")), "status": job.get_status(), - "queue": name, + "queue": queue, "creation": convert_utc_to_user_timezone(job.created_at), "color": JOB_COLORS[job.get_status()], } @@ -48,14 +44,21 @@ def get_info(view=None, queue_timeout=None, job_status=None) -> List[Dict]: queues = get_queues() for queue in queues: for job in queue.jobs: + if job_status != "all" and job.get_status() != job_status: + return + if queue_timeout != "all" and not queue.name.endswith(f":{queue_timeout}"): + return add_job(job, queue.name) elif view == "Workers": workers = get_workers() for worker in workers: current_job = worker.get_current_job() - if current_job and current_job.kwargs.get("site") == frappe.local.site: - add_job(current_job, job.origin) + if current_job: + if hasattr(current_job, "kwargs") and current_job.kwargs.get("site") == frappe.local.site: + add_job(current_job, current_job.origin) + else: + jobs.append({"queue": worker.name, "job_name": "busy", "status": "", "creation": ""}) else: jobs.append({"queue": worker.name, "job_name": "idle", "status": "", "creation": ""}) From 746e017aeff05d26a6cc87ed31aa460cd2accc47 Mon Sep 17 00:00:00 2001 From: Nikhil Kothari Date: Sat, 16 Apr 2022 14:15:59 +0530 Subject: [PATCH 12/45] fix: Fixed spelling of "Submitting" in Bulk Upload Modal (#16638) * Fixed spelling of "Submitting" * Updated translation files --- frappe/desk/doctype/bulk_update/bulk_update.py | 2 +- frappe/translations/af.csv | 2 +- frappe/translations/am.csv | 2 +- frappe/translations/ar.csv | 2 +- frappe/translations/bg.csv | 2 +- frappe/translations/bn.csv | 2 +- frappe/translations/bs.csv | 2 +- frappe/translations/ca.csv | 2 +- frappe/translations/cs.csv | 2 +- frappe/translations/da.csv | 2 +- frappe/translations/de.csv | 2 +- frappe/translations/el.csv | 2 +- frappe/translations/es.csv | 2 +- frappe/translations/et.csv | 2 +- frappe/translations/fa.csv | 2 +- frappe/translations/fi.csv | 2 +- frappe/translations/fr.csv | 2 +- frappe/translations/gu.csv | 2 +- frappe/translations/he.csv | 2 +- frappe/translations/hi.csv | 2 +- frappe/translations/hr.csv | 2 +- frappe/translations/hu.csv | 2 +- frappe/translations/id.csv | 2 +- frappe/translations/is.csv | 2 +- frappe/translations/it.csv | 2 +- frappe/translations/ja.csv | 2 +- frappe/translations/km.csv | 2 +- frappe/translations/kn.csv | 2 +- frappe/translations/ko.csv | 2 +- frappe/translations/ku.csv | 2 +- frappe/translations/lo.csv | 2 +- frappe/translations/lt.csv | 2 +- frappe/translations/lv.csv | 2 +- frappe/translations/mk.csv | 2 +- frappe/translations/ml.csv | 2 +- frappe/translations/mr.csv | 2 +- frappe/translations/ms.csv | 2 +- frappe/translations/my.csv | 2 +- frappe/translations/nl.csv | 2 +- frappe/translations/no.csv | 2 +- frappe/translations/pl.csv | 2 +- frappe/translations/ps.csv | 2 +- frappe/translations/pt.csv | 2 +- frappe/translations/ro.csv | 2 +- frappe/translations/ru.csv | 2 +- frappe/translations/rw.csv | 2 +- frappe/translations/si.csv | 2 +- frappe/translations/sk.csv | 2 +- frappe/translations/sl.csv | 2 +- frappe/translations/sq.csv | 2 +- frappe/translations/sr.csv | 2 +- frappe/translations/sv.csv | 2 +- frappe/translations/sw.csv | 2 +- frappe/translations/ta.csv | 2 +- frappe/translations/te.csv | 2 +- frappe/translations/th.csv | 2 +- frappe/translations/tr.csv | 2 +- frappe/translations/uk.csv | 2 +- frappe/translations/ur.csv | 2 +- frappe/translations/uz.csv | 2 +- frappe/translations/vi.csv | 2 +- frappe/translations/zh.csv | 2 +- frappe/translations/zh_tw.csv | 2 +- 63 files changed, 63 insertions(+), 63 deletions(-) diff --git a/frappe/desk/doctype/bulk_update/bulk_update.py b/frappe/desk/doctype/bulk_update/bulk_update.py index 4d4e83a242..dc0a88178d 100644 --- a/frappe/desk/doctype/bulk_update/bulk_update.py +++ b/frappe/desk/doctype/bulk_update/bulk_update.py @@ -46,7 +46,7 @@ def submit_cancel_or_update_docs(doctype, docnames, action="submit", data=None): message = "" if action == "submit" and doc.docstatus.is_draft(): doc.submit() - message = _("Submiting {0}").format(doctype) + message = _("Submitting {0}").format(doctype) elif action == "cancel" and doc.docstatus.is_submitted(): doc.cancel() message = _("Cancelling {0}").format(doctype) diff --git a/frappe/translations/af.csv b/frappe/translations/af.csv index fb52b4038d..88f30be096 100644 --- a/frappe/translations/af.csv +++ b/frappe/translations/af.csv @@ -2398,7 +2398,7 @@ Submit after importing,Dien na invoer in, Submit an Issue,Dien 'n uitgawe in, Submit this document to confirm,Dien hierdie dokument in om te bevestig, Submit {0} documents?,Dien {0} dokumente in, -Submiting {0},Inhandiging {0}, +Submitting {0},Inhandiging {0}, Submitted Document cannot be converted back to draft. Transition row {0},"Dokument wat ingedien is, kan nie weer na konsep omgeskakel word nie. Oorgangsreël {0}", Submitting,indiening, Subscription Notification,Inskrywing kennisgewing, diff --git a/frappe/translations/am.csv b/frappe/translations/am.csv index 4dfa1bd8cd..5321621e03 100644 --- a/frappe/translations/am.csv +++ b/frappe/translations/am.csv @@ -2398,7 +2398,7 @@ Submit after importing,ማስመጣት አስገባ, Submit an Issue,አንድ ችግር አስገባ, Submit this document to confirm,ለማረጋገጥ ይህን ሰነድ ማስገባት, Submit {0} documents?,የ {0} ሰነዶች አስገባ?, -Submiting {0},በማስገባት ላይ {0}, +Submitting {0},በማስገባት ላይ {0}, Submitted Document cannot be converted back to draft. Transition row {0},ገብቷል ሰነድ ረቂቅ ተመልሶ ሊቀየር አይችልም. የሽግግር ረድፍ {0}, Submitting,በማስገባት ላይ, Subscription Notification,የደንበኝነት ምዝገባ ማሳወቂያ, diff --git a/frappe/translations/ar.csv b/frappe/translations/ar.csv index cfd75b7dc9..95caebd35f 100644 --- a/frappe/translations/ar.csv +++ b/frappe/translations/ar.csv @@ -2398,7 +2398,7 @@ Submit after importing,تأكيد بعد الاستيراد, Submit an Issue,يقدم العدد, Submit this document to confirm,إرسال هذه الوثيقة إلى تأكيد, Submit {0} documents?,إرسال {0} وثائق؟, -Submiting {0},تقديم {0}, +Submitting {0},تقديم {0}, Submitted Document cannot be converted back to draft. Transition row {0},الوثيقة المسجلة لا يمكن تحويلها إلى مسودة row {0}, Submitting,تقديم, Subscription Notification,إشعار الاشتراك, diff --git a/frappe/translations/bg.csv b/frappe/translations/bg.csv index eaeeccdf0b..553b9eab13 100644 --- a/frappe/translations/bg.csv +++ b/frappe/translations/bg.csv @@ -2398,7 +2398,7 @@ Submit after importing,Изпратете след импортирането, Submit an Issue,Пуснете Issue, Submit this document to confirm,"Изпратете този документ, за да потвърдите", Submit {0} documents?,Изпратете {0} документи?, -Submiting {0},Изпращане на {0}, +Submitting {0},Изпращане на {0}, Submitted Document cannot be converted back to draft. Transition row {0},Изпратено Документът не може да се превръща отново да изготви проект. Поредни Transition {0}, Submitting,Изпращане, Subscription Notification,Нотификация за абонамент, diff --git a/frappe/translations/bn.csv b/frappe/translations/bn.csv index 7ca0ed2186..b6465fc555 100644 --- a/frappe/translations/bn.csv +++ b/frappe/translations/bn.csv @@ -2398,7 +2398,7 @@ Submit after importing,আমদানি করার পরে জমা দ Submit an Issue,একটি সমস্যার জমা, Submit this document to confirm,নিশ্চিত করার জন্য এই দস্তাবেজ জমা দিন, Submit {0} documents?,{0} নথি জমা দিন?, -Submiting {0},জমা দেওয়া {0}, +Submitting {0},জমা দেওয়া {0}, Submitted Document cannot be converted back to draft. Transition row {0},লগইন ডকুমেন্ট খসড়া ফিরে রূপান্তরিত করা যাবে না. স্থানান্তরণ সারিতে {0}, Submitting,জমা দেওয়ার, Subscription Notification,সাবস্ক্রিপশন বিজ্ঞপ্তি, diff --git a/frappe/translations/bs.csv b/frappe/translations/bs.csv index 9df4faa683..9ddb78dfe0 100644 --- a/frappe/translations/bs.csv +++ b/frappe/translations/bs.csv @@ -2398,7 +2398,7 @@ Submit after importing,Pošaljite nakon uvoza, Submit an Issue,Slanje problem, Submit this document to confirm,Dostavi taj dokument da potvrdi, Submit {0} documents?,Pošalji {0} dokumente?, -Submiting {0},Prijavljivanje {0}, +Submitting {0},Prijavljivanje {0}, Submitted Document cannot be converted back to draft. Transition row {0},Postavio Dokument se ne može pretvoriti natrag u nacrtu ., Submitting,Podnošenje, Subscription Notification,Obaveštenje o pretplati, diff --git a/frappe/translations/ca.csv b/frappe/translations/ca.csv index afb032ec3b..3de89a8620 100644 --- a/frappe/translations/ca.csv +++ b/frappe/translations/ca.csv @@ -2398,7 +2398,7 @@ Submit after importing,Enviar després d'importar, Submit an Issue,Presentar un problema, Submit this document to confirm,Presentar aquest document per confirmar, Submit {0} documents?,Enviar documents {0}?, -Submiting {0},Enviant {0}, +Submitting {0},Enviant {0}, Submitted Document cannot be converted back to draft. Transition row {0},Document presentat no es pot convertir de nou a redactar. Fila Transició {0}, Submitting,Presentar, Subscription Notification,Notificació de subscripció, diff --git a/frappe/translations/cs.csv b/frappe/translations/cs.csv index 2b405605fa..262d8bb1e5 100644 --- a/frappe/translations/cs.csv +++ b/frappe/translations/cs.csv @@ -2398,7 +2398,7 @@ Submit after importing,Odeslat po importu, Submit an Issue,Vložit případ podpory, Submit this document to confirm,Předloží tento dokument potvrdit, Submit {0} documents?,Odeslat {0} dokumenty?, -Submiting {0},Odeslání {0}, +Submitting {0},Odeslání {0}, Submitted Document cannot be converted back to draft. Transition row {0},Vložený dokument nemůže být konvertován na stav rozpracováno. řádek transkace {0}, Submitting,Odeslání, Subscription Notification,Oznámení předplatného, diff --git a/frappe/translations/da.csv b/frappe/translations/da.csv index 4516ed82ca..8a6398986c 100644 --- a/frappe/translations/da.csv +++ b/frappe/translations/da.csv @@ -2398,7 +2398,7 @@ Submit after importing,Indsend efter import, Submit an Issue,Indberet et problem, Submit this document to confirm,Godkend dette dokument for at bekræfte, Submit {0} documents?,Indsend {0} dokumenter?, -Submiting {0},Indsender {0}, +Submitting {0},Indsender {0}, Submitted Document cannot be converted back to draft. Transition row {0},Indsendt Dokument kan ikke konverteres tilbage til at udarbejde. Overgang rækken {0}, Submitting,Godkender, Subscription Notification,Abonnementsmeddelelse, diff --git a/frappe/translations/de.csv b/frappe/translations/de.csv index 1131a8b5d7..ed6c9fa56f 100644 --- a/frappe/translations/de.csv +++ b/frappe/translations/de.csv @@ -2410,7 +2410,7 @@ Submit after importing,Nach dem Importieren buchen, Submit an Issue,Eine Anfrage übertragen., Submit this document to confirm,"Buchen Sie dieses Dokument, um zu bestätigen", Submit {0} documents?,{0} Dokumente einreichen?, -Submiting {0},Übermitteln von {0}, +Submitting {0},Übermitteln von {0}, Submitted Document cannot be converted back to draft. Transition row {0},Buchung kann nicht in Entwurf umgewandelt werden. Zeile {0}, Submitting,Buche, Subscription Notification,Abonnementbenachrichtigung, diff --git a/frappe/translations/el.csv b/frappe/translations/el.csv index eb1eaae05f..d966526e44 100644 --- a/frappe/translations/el.csv +++ b/frappe/translations/el.csv @@ -2398,7 +2398,7 @@ Submit after importing,Υποβολή μετά την εισαγωγή, Submit an Issue,Υποβολή Θέματος, Submit this document to confirm,Υποβολή αυτό το έγγραφο για να επιβεβαιώσετε, Submit {0} documents?,Υποβάλετε {0} έγγραφα;, -Submiting {0},Υποβολή {0}, +Submitting {0},Υποβολή {0}, Submitted Document cannot be converted back to draft. Transition row {0},Το υποβλημένο έγγραφο δεν μπορεί να μετατραπεί ξανά σε προσχέδιο. Γραμμή μετάβασης {0}, Submitting,Υποβολή, Subscription Notification,Ειδοποίηση συνδρομής, diff --git a/frappe/translations/es.csv b/frappe/translations/es.csv index 4ccd85704e..73346e49f9 100644 --- a/frappe/translations/es.csv +++ b/frappe/translations/es.csv @@ -2398,7 +2398,7 @@ Submit after importing,Enviar Después de Importar, Submit an Issue,Validar una incidencia, Submit this document to confirm,Presentar este documento para confirmar, Submit {0} documents?,¿Presentar {0} documentos?, -Submiting {0},Envío de {0}, +Submitting {0},Envío de {0}, Submitted Document cannot be converted back to draft. Transition row {0},"El documento validado no se puede revertir a borrador, en la línea de transición {0}.", Submitting,Validando, Subscription Notification,Notificación de Suscripción, diff --git a/frappe/translations/et.csv b/frappe/translations/et.csv index 899e502d1e..c4ca980045 100644 --- a/frappe/translations/et.csv +++ b/frappe/translations/et.csv @@ -2398,7 +2398,7 @@ Submit after importing,Esita pärast importimist, Submit an Issue,Esita probleemist, Submit this document to confirm,Esita selle dokumendi kinnitamiseks, Submit {0} documents?,Esitada {0} dokumente?, -Submiting {0},{0} esitamine, +Submitting {0},{0} esitamine, Submitted Document cannot be converted back to draft. Transition row {0},Esitatud dokument ei saa muuta tagasi koostada. Üleminek rida {0}, Submitting,Esitades, Subscription Notification,Tellimuse teatis, diff --git a/frappe/translations/fa.csv b/frappe/translations/fa.csv index a2d3a5f221..99df9a9dca 100644 --- a/frappe/translations/fa.csv +++ b/frappe/translations/fa.csv @@ -2398,7 +2398,7 @@ Submit after importing,ارسال پس از وارد کردن, Submit an Issue,ثبت کردن شماره, Submit this document to confirm,ارسال این سند را به اعلام, Submit {0} documents?,ارسال اسناد {0}؟, -Submiting {0},ارسال {0}, +Submitting {0},ارسال {0}, Submitted Document cannot be converted back to draft. Transition row {0},سند فرستاده ممکن نیست تبدیل به پیش نویس است. ردیف گذار {0}, Submitting,ارسال, Subscription Notification,اعلان اشتراک, diff --git a/frappe/translations/fi.csv b/frappe/translations/fi.csv index 991b22e302..2c62458fbb 100644 --- a/frappe/translations/fi.csv +++ b/frappe/translations/fi.csv @@ -2398,7 +2398,7 @@ Submit after importing,Lähetä tuonnin jälkeen, Submit an Issue,Vahvista aihe, Submit this document to confirm,Vahvista hyväksyväsi tämän dokumentin, Submit {0} documents?,Lähetä {0} asiakirjoja?, -Submiting {0},Lähettäminen {0}, +Submitting {0},Lähettäminen {0}, Submitted Document cannot be converted back to draft. Transition row {0},Vahvistettua dokumenttia ei voi muuttaa takaisin luonnokseksi. Tapahtuman rivi {0}, Submitting,Vahvistetaan, Subscription Notification,Tilausilmoitus, diff --git a/frappe/translations/fr.csv b/frappe/translations/fr.csv index be0d6a6892..06a2473d3b 100644 --- a/frappe/translations/fr.csv +++ b/frappe/translations/fr.csv @@ -2399,7 +2399,7 @@ Submit after importing,Valider après l'import, Submit an Issue,Valider un ticket, Submit this document to confirm,Valider ce document pour confirmer, Submit {0} documents?,Valider {0} documents ?, -Submiting {0},Validation de {0}, +Submitting {0},Validation de {0}, Submitted Document cannot be converted back to draft. Transition row {0},Document Valider ne peut pas être reconvertis en Brouillon. Ligne de transition {0}, Submitting,Validation, Subscription Notification,Notification d'abonnement, diff --git a/frappe/translations/gu.csv b/frappe/translations/gu.csv index c2c485c2e7..e13637d26a 100644 --- a/frappe/translations/gu.csv +++ b/frappe/translations/gu.csv @@ -2398,7 +2398,7 @@ Submit after importing,આયાત કર્યા પછી સબમિટ Submit an Issue,એક મુદ્દો સબમિટ, Submit this document to confirm,તેની ખાતરી કરવા માટે આ દસ્તાવેજ સબમિટ, Submit {0} documents?,{0} દસ્તાવેજો સબમિટ કરીએ?, -Submiting {0},સબમિટ કરવું {0}, +Submitting {0},સબમિટ કરવું {0}, Submitted Document cannot be converted back to draft. Transition row {0},સબમિટ દસ્તાવેજ મુસદ્દો તૈયાર કરવા માટે પાછા રૂપાંતરિત કરી શકાતા નથી. ટ્રાન્ઝિશન પંક્તિ {0}, Submitting,સબમિટ, Subscription Notification,સબ્સ્ક્રિપ્શન સૂચના, diff --git a/frappe/translations/he.csv b/frappe/translations/he.csv index c78ff6c556..dda9f2b233 100644 --- a/frappe/translations/he.csv +++ b/frappe/translations/he.csv @@ -2398,7 +2398,7 @@ Submit after importing,הגש לאחר הייבוא, Submit an Issue,שלח גיליון, Submit this document to confirm,שלח את המסמך הזה כדי לאשר, Submit {0} documents?,להגיש {0} מסמכים?, -Submiting {0},מגיש {0}, +Submitting {0},מגיש {0}, Submitted Document cannot be converted back to draft. Transition row {0},מסמך שהוגש לא ניתן להמיר חזרה לטיוטה. שורת מעבר {0}, Submitting,הגשה, Subscription Notification,הודעת מנוי, diff --git a/frappe/translations/hi.csv b/frappe/translations/hi.csv index 35b8ec1bd9..fcf3a32263 100644 --- a/frappe/translations/hi.csv +++ b/frappe/translations/hi.csv @@ -2398,7 +2398,7 @@ Submit after importing,आयात करने के बाद सबमि Submit an Issue,किसी समस्या सबमिट, Submit this document to confirm,इस बात की पुष्टि करने के लिए इस दस्तावेज जमा करें, Submit {0} documents?,{0} दस्तावेज़ सबमिट करें?, -Submiting {0},जमा करना {0}, +Submitting {0},जमा करना {0}, Submitted Document cannot be converted back to draft. Transition row {0},प्रस्तुत दस्तावेज का मसौदा तैयार करने के लिए वापस नहीं बदला जा सकता ., Submitting,भेजने से, Subscription Notification,सदस्यता अधिसूचना, diff --git a/frappe/translations/hr.csv b/frappe/translations/hr.csv index ff9cf5453a..4846876c82 100644 --- a/frappe/translations/hr.csv +++ b/frappe/translations/hr.csv @@ -2398,7 +2398,7 @@ Submit after importing,Slanje nakon uvoza, Submit an Issue,Slanje problem, Submit this document to confirm,Pošaljite ovaj dokument za potvrdu, Submit {0} documents?,Pošaljite {0} dokumente?, -Submiting {0},Slanje {0}, +Submitting {0},Slanje {0}, Submitted Document cannot be converted back to draft. Transition row {0},Postavio Dokument se ne može pretvoriti natrag u nacrtu ., Submitting,Slanje, Subscription Notification,Obavijest o pretplati, diff --git a/frappe/translations/hu.csv b/frappe/translations/hu.csv index 83e292b672..677447b809 100644 --- a/frappe/translations/hu.csv +++ b/frappe/translations/hu.csv @@ -2398,7 +2398,7 @@ Submit after importing,Elküldés az importálás után, Submit an Issue,Jelentsen egy problémát, Submit this document to confirm,Küldje el ezt a dokumentumot megerősítésre, Submit {0} documents?,Küldje el a {0} dokumentumokat?, -Submiting {0},Benyúj {0}, +Submitting {0},Benyúj {0}, Submitted Document cannot be converted back to draft. Transition row {0},Benyújtott dokumentumot nem lehet visszaalakítani tervezetté. Átvezetési sor {0}, Submitting,Benyújtása, Subscription Notification,Előfizetési értesítés, diff --git a/frappe/translations/id.csv b/frappe/translations/id.csv index c4eae3e145..ff46ec82a9 100644 --- a/frappe/translations/id.csv +++ b/frappe/translations/id.csv @@ -2398,7 +2398,7 @@ Submit after importing,Kirim setelah mengimpor, Submit an Issue,Menyerahkan Masalah, Submit this document to confirm,Menyerahkan dokumen ini untuk mengkonfirmasi, Submit {0} documents?,Kirim {0} dokumen?, -Submiting {0},Mengajukan {0}, +Submitting {0},Mengajukan {0}, Submitted Document cannot be converted back to draft. Transition row {0},Dikirim Dokumen tidak dapat dikonversi kembali untuk menyusun. Transisi baris {0}, Submitting,Mengirimkan, Subscription Notification,Pemberitahuan Berlangganan, diff --git a/frappe/translations/is.csv b/frappe/translations/is.csv index fd0b552701..f870b63254 100644 --- a/frappe/translations/is.csv +++ b/frappe/translations/is.csv @@ -2398,7 +2398,7 @@ Submit after importing,Sendu inn eftir innflutning, Submit an Issue,Leggja mál, Submit this document to confirm,Senda þessu skjali til að staðfesta, Submit {0} documents?,Sendu inn {0} skjöl?, -Submiting {0},Sending {0}, +Submitting {0},Sending {0}, Submitted Document cannot be converted back to draft. Transition row {0},Lögð Document ekki hægt að breyta aftur í drög. Umskipti róður {0}, Submitting,Sendi, Subscription Notification,Tilkynning um áskrift, diff --git a/frappe/translations/it.csv b/frappe/translations/it.csv index 1d4c1af0f2..3a46044681 100644 --- a/frappe/translations/it.csv +++ b/frappe/translations/it.csv @@ -2398,7 +2398,7 @@ Submit after importing,Invia dopo l'importazione, Submit an Issue,Segnala un Problema, Submit this document to confirm,Presenta questo documento per confermare, Submit {0} documents?,Invia {0} documenti?, -Submiting {0},Invio {0}, +Submitting {0},Invio {0}, Submitted Document cannot be converted back to draft. Transition row {0},I documenti Confermati non possono essere riconvertiti in Bozza. Riga di transazione{0}, Submitting,In fase di conferma, Subscription Notification,Notifica di iscrizione, diff --git a/frappe/translations/ja.csv b/frappe/translations/ja.csv index 35029e0058..8a1e3d0884 100644 --- a/frappe/translations/ja.csv +++ b/frappe/translations/ja.csv @@ -2398,7 +2398,7 @@ Submit after importing,インポート後に提出する, Submit an Issue,課題を投稿, Submit this document to confirm,確認のため、この文書を提出, Submit {0} documents?,{0}文書を提出しますか?, -Submiting {0},{0}を送信中です, +Submitting {0},{0}を送信中です, Submitted Document cannot be converted back to draft. Transition row {0},提出されたドキュメントは、ドラフトに変換しなおすことができません。遷移行{0}, Submitting,提出, Subscription Notification,購読通知, diff --git a/frappe/translations/km.csv b/frappe/translations/km.csv index 5e8e1fc2d5..6b7ad32f40 100644 --- a/frappe/translations/km.csv +++ b/frappe/translations/km.csv @@ -2398,7 +2398,7 @@ Submit after importing,ដាក់ស្នើបន្ទាប់ពីកា Submit an Issue,ដាក់ស្នើនូវបញ្ហា, Submit this document to confirm,ដាក់ស្នើឯកសារនេះដើម្បីបញ្ជាក់, Submit {0} documents?,ដាក់ស្នើ {0} ឯកសារ?, -Submiting {0},ដាក់ស្នើ {0}, +Submitting {0},ដាក់ស្នើ {0}, Submitted Document cannot be converted back to draft. Transition row {0},ឯកសារដែលបានដាក់ស្នើមិនអាចត្រូវបានបម្លែងត្រឡប់មកវិញដើម្បីធ្វើសេចក្តីព្រាង។ ជួរដេកដែលបានផ្លាស់ប្តូរ {0}, Submitting,ដាក់ស្នើ, Subscription Notification,សេចក្ដីជូនដំណឹងការជាវ, diff --git a/frappe/translations/kn.csv b/frappe/translations/kn.csv index 616732b9de..ef5e06a016 100644 --- a/frappe/translations/kn.csv +++ b/frappe/translations/kn.csv @@ -2398,7 +2398,7 @@ Submit after importing,ಆಮದು ಮಾಡಿದ ನಂತರ ಸಲ್ಲಿ Submit an Issue,ಸಮಸ್ಯೆಯನ್ನು ಸಲ್ಲಿಸಿ, Submit this document to confirm,ಖಚಿತಪಡಿಸಲು ದಾಖಲೆ ಸಲ್ಲಿಸಿ, Submit {0} documents?,{0} ದಾಖಲೆಗಳನ್ನು ಸಲ್ಲಿಸಿ?, -Submiting {0},{0}, +Submitting {0},{0}, Submitted Document cannot be converted back to draft. Transition row {0},ಸಲ್ಲಿಸಿದ ಡಾಕ್ಯುಮೆಂಟ್ ಕರಡುಪ್ರತಿಯನ್ನು ಪರಿವರ್ತಿಸಬಹುದಾದ ಸಾಧ್ಯವಿಲ್ಲ ., Submitting,ಸಲ್ಲಿಸಲಾಗುತ್ತಿದೆ, Subscription Notification,ಚಂದಾದಾರಿಕೆ ಅಧಿಸೂಚನೆ, diff --git a/frappe/translations/ko.csv b/frappe/translations/ko.csv index 4d197b2d03..a40c2b787d 100644 --- a/frappe/translations/ko.csv +++ b/frappe/translations/ko.csv @@ -2398,7 +2398,7 @@ Submit after importing,가져온 후 제출, Submit an Issue,문제 제출, Submit this document to confirm,확인하기 위해이 문서를 제출, Submit {0} documents?,{0} 문서를 제출 하시겠습니까?, -Submiting {0},{0} 제출 중입니다., +Submitting {0},{0} 제출 중입니다., Submitted Document cannot be converted back to draft. Transition row {0},제출 된 문서는 초안으로 변환 할 수 없습니다. 전환 행 {0}, Submitting,제출, Subscription Notification,구독 알림, diff --git a/frappe/translations/ku.csv b/frappe/translations/ku.csv index 93f6b95f69..a30ffc115d 100644 --- a/frappe/translations/ku.csv +++ b/frappe/translations/ku.csv @@ -2398,7 +2398,7 @@ Submit after importing,Piştî hilberandinê, Submit an Issue,Submit an Dozî Kurd, Submit this document to confirm,Submit vê belgeyê de ji bo piştrast, Submit {0} documents?,Belgeyên {0} bişînin?, -Submiting {0},Submit {0}, +Submitting {0},Submit {0}, Submitted Document cannot be converted back to draft. Transition row {0},Dokumentê radestkirî paş ne venegerin kişandina. row Transition {0}, Submitting,Radestkirina, Subscription Notification,Agahdariya Mirovan, diff --git a/frappe/translations/lo.csv b/frappe/translations/lo.csv index 006bee49a8..fceaa5a31f 100644 --- a/frappe/translations/lo.csv +++ b/frappe/translations/lo.csv @@ -2398,7 +2398,7 @@ Submit after importing,ສົ່ງຫຼັງຈາກການນໍາເຂ Submit an Issue,ຍື່ນສະເຫນີບັນຫາ, Submit this document to confirm,ສົ່ງເອກະສານເພື່ອຍືນຍັນ, Submit {0} documents?,ສົ່ງ {0} ເອກະສານ?, -Submiting {0},ການສົ່ງ {0}, +Submitting {0},ການສົ່ງ {0}, Submitted Document cannot be converted back to draft. Transition row {0},ເອກະສານສົ່ງບໍ່ສາມາດໄດ້ຮັບການປ່ຽນໃຈເຫລື້ອມໃສກັບຄືນໄປບ່ອນຮ່າງ. ຕິດຕໍ່ກັນການຫັນປ່ຽນ {0}, Submitting,ສົ່ງ, Subscription Notification,Notification Subscription, diff --git a/frappe/translations/lt.csv b/frappe/translations/lt.csv index d6c743c1bf..22979f9b69 100644 --- a/frappe/translations/lt.csv +++ b/frappe/translations/lt.csv @@ -2398,7 +2398,7 @@ Submit after importing,Pateikti importuojant, Submit an Issue,Pateikti numeris, Submit this document to confirm,Pateikti šį dokumentą patvirtinti, Submit {0} documents?,Pateikti {0} dokumentus?, -Submiting {0},Pateikiama {0}, +Submitting {0},Pateikiama {0}, Submitted Document cannot be converted back to draft. Transition row {0},Pateiktas dokumentas negali būti konvertuojami atgal į projektą. Perėjimas eilutė {0}, Submitting,Siunčiamas, Subscription Notification,Prenumeratos pranešimas, diff --git a/frappe/translations/lv.csv b/frappe/translations/lv.csv index 4ea90df6e2..1b14a6c5da 100644 --- a/frappe/translations/lv.csv +++ b/frappe/translations/lv.csv @@ -2398,7 +2398,7 @@ Submit after importing,Iesniegt pēc importēšanas, Submit an Issue,Iesniegt Problēma, Submit this document to confirm,"Iesniegt šo dokumentu, lai apstiprinātu", Submit {0} documents?,Iesniegt {0} dokumentus?, -Submiting {0},Iesniedzot {0}, +Submitting {0},Iesniedzot {0}, Submitted Document cannot be converted back to draft. Transition row {0},Iesniegtie Dokumentu nevar pārvērst atpakaļ uz projektu. Pāreja rinda {0}, Submitting,Iesniedzot, Subscription Notification,Abonēšanas paziņojums, diff --git a/frappe/translations/mk.csv b/frappe/translations/mk.csv index 20d42ae7f5..6d171823fb 100644 --- a/frappe/translations/mk.csv +++ b/frappe/translations/mk.csv @@ -2398,7 +2398,7 @@ Submit after importing,Прати после увоз, Submit an Issue,Поднесете ја претставува проблем, Submit this document to confirm,Го достави овој документ кој ја потврдува, Submit {0} documents?,Поднесете {0} документи?, -Submiting {0},Поднесување {0}, +Submitting {0},Поднесување {0}, Submitted Document cannot be converted back to draft. Transition row {0},Поднесен документ не може да се конвертира назад во Предлог. Транзициски ред {0}, Submitting,Поднесување, Subscription Notification,Известување за претплата, diff --git a/frappe/translations/ml.csv b/frappe/translations/ml.csv index 8a8ae15b91..6f0a22b53c 100644 --- a/frappe/translations/ml.csv +++ b/frappe/translations/ml.csv @@ -2398,7 +2398,7 @@ Submit after importing,ഇറക്കുമതി ചെയ്ത ശേഷം Submit an Issue,ഒരു പ്രശ്നം സമർപ്പിക്കുക, Submit this document to confirm,സ്ഥിരീകരിക്കുന്നതിന് ഈ പ്രമാണം സമർപ്പിക്കുക, Submit {0} documents?,പ്രമാണങ്ങൾ {0} സമർപ്പിക്കണോ?, -Submiting {0},{0} നൽകൽ, +Submitting {0},{0} നൽകൽ, Submitted Document cannot be converted back to draft. Transition row {0},സമർപ്പിച്ച ഡോക്യുമെന്റ് തിരികെ കരട് പരിവർത്തനം ചെയ്യാൻ കഴിയില്ല. ട്രാൻസിഷൻ വരി {0}, Submitting,സമർപ്പിക്കുന്നു, Subscription Notification,സബ്സ്ക്രിപ്ഷൻ അറിയിപ്പ്, diff --git a/frappe/translations/mr.csv b/frappe/translations/mr.csv index 6e98f94434..a4d5dadfd6 100644 --- a/frappe/translations/mr.csv +++ b/frappe/translations/mr.csv @@ -2398,7 +2398,7 @@ Submit after importing,आयात केल्यानंतर सबमि Submit an Issue,एक समस्या सादर करा, Submit this document to confirm,पुष्टी करण्यासाठी या दस्तऐवज सादर, Submit {0} documents?,{0} दस्तऐवज सबमिट करायचे?, -Submiting {0},सबमिट करणे {0}, +Submitting {0},सबमिट करणे {0}, Submitted Document cannot be converted back to draft. Transition row {0},सबमिट दस्तऐवज मसुद्यावर परत रूपांतरीत केले जाऊ शकत नाही. स्थित्यंतर सलग {0}, Submitting,सादर करत आहे, Subscription Notification,सदस्यता अधिसूचना, diff --git a/frappe/translations/ms.csv b/frappe/translations/ms.csv index 00c97f0a20..c562355b75 100644 --- a/frappe/translations/ms.csv +++ b/frappe/translations/ms.csv @@ -2398,7 +2398,7 @@ Submit after importing,Hantar selepas mengimport, Submit an Issue,Hantar Isu, Submit this document to confirm,Mengemukakan dokumen ini untuk mengesahkan, Submit {0} documents?,Hantar {0} dokumen?, -Submiting {0},Menghantar {0}, +Submitting {0},Menghantar {0}, Submitted Document cannot be converted back to draft. Transition row {0},Dokumen yang dihantar tidak boleh ditukar kembali untuk merangka. Berturut-turut peralihan {0}, Submitting,Mengemukakan, Subscription Notification,Pemberitahuan Langganan, diff --git a/frappe/translations/my.csv b/frappe/translations/my.csv index 357a688a03..724e65e4c5 100644 --- a/frappe/translations/my.csv +++ b/frappe/translations/my.csv @@ -2398,7 +2398,7 @@ Submit after importing,တင်သွင်းပြီးနောက် Submi Submit an Issue,တစ်ဦး Issue Submit, Submit this document to confirm,အတည်ပြုဖို့ဤစာရွက်စာတမ်း Submit, Submit {0} documents?,{0} စာရွက်စာတမ်းများ Submit?, -Submiting {0},Submiting {0}, +Submitting {0},Submitting {0}, Submitted Document cannot be converted back to draft. Transition row {0},Submitted Document ဖိုင်ပြန်မူကြမ်းအဖြစ်ပြောင်းလဲမရနိုင်ပါ။ အကူးအပြောင်းအတန်း {0}, Submitting,တင်ပြနေသည်, Subscription Notification,subscription သတိပေးချက်, diff --git a/frappe/translations/nl.csv b/frappe/translations/nl.csv index 850faa57a7..677cc279aa 100644 --- a/frappe/translations/nl.csv +++ b/frappe/translations/nl.csv @@ -2398,7 +2398,7 @@ Submit after importing,Verzenden na importeren, Submit an Issue,Dien een Issue in, Submit this document to confirm,Verzend dit document om te bevestigen, Submit {0} documents?,{0} documenten verzenden?, -Submiting {0},{0} indienen, +Submitting {0},{0} indienen, Submitted Document cannot be converted back to draft. Transition row {0},Ingediend Document kan niet teruggezet worden naar Draft. Transitie rij {0}, Submitting,Indienen, Subscription Notification,Abonnementsmelding, diff --git a/frappe/translations/no.csv b/frappe/translations/no.csv index a91d09ea54..03564cc5f2 100644 --- a/frappe/translations/no.csv +++ b/frappe/translations/no.csv @@ -2398,7 +2398,7 @@ Submit after importing,Send etter import, Submit an Issue,Send inn et problem, Submit this document to confirm,Send dette dokumentet for å bekrefte, Submit {0} documents?,Send {0} dokumenter?, -Submiting {0},Innlevering {0}, +Submitting {0},Innlevering {0}, Submitted Document cannot be converted back to draft. Transition row {0},Skrevet Dokument kan ikke konverteres tilbake til utkast. Transition rad {0}, Submitting,Sende inn, Subscription Notification,Abonnementsvarsling, diff --git a/frappe/translations/pl.csv b/frappe/translations/pl.csv index 7e6b10385e..fb2f52179f 100644 --- a/frappe/translations/pl.csv +++ b/frappe/translations/pl.csv @@ -2398,7 +2398,7 @@ Submit after importing,Prześlij po zaimportowaniu, Submit an Issue,Zgłoś problem, Submit this document to confirm,"Prześlij ten dokument, aby potwierdzić", Submit {0} documents?,Prześlij dokumenty {0}?, -Submiting {0},Przesyłanie: {0}, +Submitting {0},Przesyłanie: {0}, Submitted Document cannot be converted back to draft. Transition row {0},Przedłożony dokument nie może być przekształcony z powrotem na szkic. Przejście wiersz {0}, Submitting,Złożenie, Subscription Notification,Powiadomienie o subskrypcji, diff --git a/frappe/translations/ps.csv b/frappe/translations/ps.csv index a17d94fb2b..66380b546a 100644 --- a/frappe/translations/ps.csv +++ b/frappe/translations/ps.csv @@ -2398,7 +2398,7 @@ Submit after importing,د واردولو وروسته وسپارل, Submit an Issue,یو Issue سپارل, Submit this document to confirm,د دې سند سپارل د تایید, Submit {0} documents?,د {0} اسناد تسلیم کړئ؟, -Submiting {0},تسلیم کول {0}, +Submitting {0},تسلیم کول {0}, Submitted Document cannot be converted back to draft. Transition row {0},ته وسپارل سند نه شي بېرته بدل قانون مسوده. د لیږد د قطار {0}, Submitting,ته وسپارل شول, Subscription Notification,د ګډون سپارل, diff --git a/frappe/translations/pt.csv b/frappe/translations/pt.csv index a8d04f5532..cf4a72e458 100644 --- a/frappe/translations/pt.csv +++ b/frappe/translations/pt.csv @@ -2398,7 +2398,7 @@ Submit after importing,Enviar depois de importar, Submit an Issue,Enviar um Incidente, Submit this document to confirm,Submeter este documento para confirmar, Submit {0} documents?,Envie {0} documentos?, -Submiting {0},Enviando {0}, +Submitting {0},Enviando {0}, Submitted Document cannot be converted back to draft. Transition row {0},Documento apresentado não pode ser convertido de volta para rascunho., Submitting,Submeter, Subscription Notification,Notificação de Subscrição, diff --git a/frappe/translations/ro.csv b/frappe/translations/ro.csv index 1f0b8e8bab..89a7b2edb6 100644 --- a/frappe/translations/ro.csv +++ b/frappe/translations/ro.csv @@ -2398,7 +2398,7 @@ Submit after importing,Trimiteți după import, Submit an Issue,Prezenta eroare, Submit this document to confirm,Să prezinte acest document pentru a confirma, Submit {0} documents?,Trimiteți {0} documente?, -Submiting {0},Trimiterea {0}, +Submitting {0},Trimiterea {0}, Submitted Document cannot be converted back to draft. Transition row {0},A prezentat documente nu pot fi convertite înapoi să elaboreze. Tranziție rând {0}, Submitting,Inscrierea, Subscription Notification,Notificare privind abonamentul, diff --git a/frappe/translations/ru.csv b/frappe/translations/ru.csv index 6c7db7d6a4..197f2cb26d 100644 --- a/frappe/translations/ru.csv +++ b/frappe/translations/ru.csv @@ -2398,7 +2398,7 @@ Submit after importing,Отправить после импорта, Submit an Issue,Отправить вопрос, Submit this document to confirm,Провести этот документ для подтверждения, Submit {0} documents?,Отправить {0} документы?, -Submiting {0},Помещение {0}, +Submitting {0},Помещение {0}, Submitted Document cannot be converted back to draft. Transition row {0},Проведенный Документ не может быть преобразован обратно в проект. Переходная строка {0}, Submitting,Проведение, Subscription Notification,Уведомление о подписке, diff --git a/frappe/translations/rw.csv b/frappe/translations/rw.csv index 002d22e72b..c33436fd2a 100644 --- a/frappe/translations/rw.csv +++ b/frappe/translations/rw.csv @@ -2398,7 +2398,7 @@ Submit after importing,Tanga nyuma yo gutumiza mu mahanga, Submit an Issue,Tanga ikibazo, Submit this document to confirm,Tanga iyi nyandiko kugirango wemeze, Submit {0} documents?,Tanga {0} inyandiko?, -Submiting {0},Kohereza {0}, +Submitting {0},Kohereza {0}, Submitted Document cannot be converted back to draft. Transition row {0},Inyandiko yatanzwe ntishobora guhindurwa mugushinga. Umurongo w'inzibacyuho {0}, Submitting,Kohereza, Subscription Notification,Kumenyesha kwiyandikisha, diff --git a/frappe/translations/si.csv b/frappe/translations/si.csv index 12d8c7e48b..8ee60ca34f 100644 --- a/frappe/translations/si.csv +++ b/frappe/translations/si.csv @@ -2398,7 +2398,7 @@ Submit after importing,ආනයනයෙන් පසු ඉදිරිපත Submit an Issue,ක නිකුත් ඉදිරිපත්, Submit this document to confirm,තහවුරු කර ගැනීමට මෙම ලේඛනය ඉදිරිපත් කළ, Submit {0} documents?,ලේඛන {0} ඉදිරිපත් කරන්නද?, -Submiting {0},ඉදිරිපත් කිරීම {0}, +Submitting {0},ඉදිරිපත් කිරීම {0}, Submitted Document cannot be converted back to draft. Transition row {0},ඉදිරිපත් ලේඛන නැවත කෙටුම්පත් පරිවර්තනය කළ නොහැක. සංක්රමණය පේළිය {0}, Submitting,ඉදිරිපත්, Subscription Notification,දායකත්ව දැනුම්දීම, diff --git a/frappe/translations/sk.csv b/frappe/translations/sk.csv index 7583eec96e..d167f710a6 100644 --- a/frappe/translations/sk.csv +++ b/frappe/translations/sk.csv @@ -2398,7 +2398,7 @@ Submit after importing,Odoslať po importovaní, Submit an Issue,Vložit případ podpory, Submit this document to confirm,Predloží dokument potvrdiť, Submit {0} documents?,Odoslať {0} dokumenty?, -Submiting {0},Odoslanie {0}, +Submitting {0},Odoslanie {0}, Submitted Document cannot be converted back to draft. Transition row {0},Potvrdený dokument nemôže byť zvrátený na stav rozpracovania. Riadok transakcie {0}, Submitting,Potvrdzované, Subscription Notification,Upozornenie na odber, diff --git a/frappe/translations/sl.csv b/frappe/translations/sl.csv index e3eb3018c5..ef6d0aba90 100644 --- a/frappe/translations/sl.csv +++ b/frappe/translations/sl.csv @@ -2398,7 +2398,7 @@ Submit after importing,Po uvozu pošljite, Submit an Issue,Predloži vprašanje, Submit this document to confirm,Predloži ta dokument za potrditev, Submit {0} documents?,Pošljite {0} dokumente?, -Submiting {0},Pošiljanje {0}, +Submitting {0},Pošiljanje {0}, Submitted Document cannot be converted back to draft. Transition row {0},Predložena listina ni mogoče pretvoriti nazaj v osnutek. Prehod vrstica {0}, Submitting,Pošiljanje, Subscription Notification,Obvestilo o naročnini, diff --git a/frappe/translations/sq.csv b/frappe/translations/sq.csv index b26b104850..cf2c1a5f4e 100644 --- a/frappe/translations/sq.csv +++ b/frappe/translations/sq.csv @@ -2398,7 +2398,7 @@ Submit after importing,Dërgo pas importimit, Submit an Issue,Submit një çështje, Submit this document to confirm,Submit këtë dokument për të konfirmuar, Submit {0} documents?,Dërgoni {0} dokumente?, -Submiting {0},Dërgimi i {0}, +Submitting {0},Dërgimi i {0}, Submitted Document cannot be converted back to draft. Transition row {0},Dokumenti i paraqitur nuk mund të kthehet përsëri për të hartuar. Rresht Tranzicioni {0}, Submitting,Dorëzimi, Subscription Notification,Njoftimi i abonimit, diff --git a/frappe/translations/sr.csv b/frappe/translations/sr.csv index 0f3de31315..59f41cedb7 100644 --- a/frappe/translations/sr.csv +++ b/frappe/translations/sr.csv @@ -2398,7 +2398,7 @@ Submit after importing,Пошаљи после увоза, Submit an Issue,Представьте о проблеме, Submit this document to confirm,Достави овај документ да потврди, Submit {0} documents?,Пошаљи {0} документе?, -Submiting {0},Пошаљи {0}, +Submitting {0},Пошаљи {0}, Submitted Document cannot be converted back to draft. Transition row {0},Поднет документ не може да се конвертује у изради ., Submitting,Подношење, Subscription Notification,Обавештење о претплати, diff --git a/frappe/translations/sv.csv b/frappe/translations/sv.csv index 8ab480d659..5c6a4fc20f 100644 --- a/frappe/translations/sv.csv +++ b/frappe/translations/sv.csv @@ -2398,7 +2398,7 @@ Submit after importing,Skicka efter import, Submit an Issue,Skicka en händelse, Submit this document to confirm,Skicka det här dokumentet för att bekräfta, Submit {0} documents?,Skicka {0} dokument?, -Submiting {0},Inlämning {0}, +Submitting {0},Inlämning {0}, Submitted Document cannot be converted back to draft. Transition row {0},Inskickat dokument kan inte konverteras tillbaka till utkast. Övergångs raden {0}, Submitting,Lämna in, Subscription Notification,Anmälan om anmälan, diff --git a/frappe/translations/sw.csv b/frappe/translations/sw.csv index cbe7ddd65c..21d0e364d3 100644 --- a/frappe/translations/sw.csv +++ b/frappe/translations/sw.csv @@ -2398,7 +2398,7 @@ Submit after importing,Tuma baada ya kuagiza, Submit an Issue,Tuma Suala, Submit this document to confirm,Tuma hati hii ili kuthibitisha, Submit {0} documents?,Wasilisha {0} nyaraka?, -Submiting {0},Kuwasilisha {0}, +Submitting {0},Kuwasilisha {0}, Submitted Document cannot be converted back to draft. Transition row {0},Hati iliyotumwa haiwezi kubadilishwa kwenye rasimu. Mstari wa mpito {0}, Submitting,Kuwasilisha, Subscription Notification,Arifa ya Usajili, diff --git a/frappe/translations/ta.csv b/frappe/translations/ta.csv index cdc6fcd468..faf857b714 100644 --- a/frappe/translations/ta.csv +++ b/frappe/translations/ta.csv @@ -2398,7 +2398,7 @@ Submit after importing,இறக்குமதி செய்த பின் Submit an Issue,ஒரு Issue சமர்ப்பிக்கவும், Submit this document to confirm,உறுதிப்படுத்த இந்த ஆவணம் சமர்ப்பிக்க, Submit {0} documents?,ஆவணங்களை {0} சமர்ப்பிக்கவா?, -Submiting {0},சமர்ப்பித்தல் {0}, +Submitting {0},சமர்ப்பித்தல் {0}, Submitted Document cannot be converted back to draft. Transition row {0},இந்த ஆவணத்தின் வரைவு மீண்டும் மாற்ற முடியாது ., Submitting,சமர்ப்பிக்கும், Subscription Notification,சந்தா அறிவிப்பு, diff --git a/frappe/translations/te.csv b/frappe/translations/te.csv index 6018977bad..df72e7e710 100644 --- a/frappe/translations/te.csv +++ b/frappe/translations/te.csv @@ -2398,7 +2398,7 @@ Submit after importing,దిగుమతి చేసిన తర్వాత Submit an Issue,ఒక ఇష్యూ సమర్పించండి, Submit this document to confirm,నిర్ధారించుకోవటానికి ఈ పత్రాన్ని సమర్పించి, Submit {0} documents?,{0} పత్రాలను సమర్పించాలా?, -Submiting {0},సమర్పణ {0}, +Submitting {0},సమర్పణ {0}, Submitted Document cannot be converted back to draft. Transition row {0},Submitted డాక్యుమెంట్ ముసాయిదా తిరిగి మార్చాల్సిన సాధ్యం కాదు. ట్రాన్సిషన్ వరుసగా {0}, Submitting,సమర్పిస్తోంది, Subscription Notification,సబ్స్క్రిప్షన్ నోటిఫికేషన్, diff --git a/frappe/translations/th.csv b/frappe/translations/th.csv index 3294672a1b..f168e5ac00 100644 --- a/frappe/translations/th.csv +++ b/frappe/translations/th.csv @@ -2398,7 +2398,7 @@ Submit after importing,ส่งหลังจากการนำเข้า Submit an Issue,ส่ง ออก, Submit this document to confirm,ส่งเอกสารนี้เพื่อยืนยัน, Submit {0} documents?,ส่ง {0} เอกสาร?, -Submiting {0},การส่ง {0}, +Submitting {0},การส่ง {0}, Submitted Document cannot be converted back to draft. Transition row {0},ส่ง เอกสาร ไม่สามารถแปลง กลับไปที่ ร่าง, Submitting,ส่ง, Subscription Notification,ประกาศการบอกรับเป็นสมาชิก, diff --git a/frappe/translations/tr.csv b/frappe/translations/tr.csv index 91d651be8f..a16e68f640 100644 --- a/frappe/translations/tr.csv +++ b/frappe/translations/tr.csv @@ -2398,7 +2398,7 @@ Submit after importing,İçe aktarmadan sonra gönderme, Submit an Issue,Bir Sorun Gönder, Submit this document to confirm,onaylamak için bu belge göndermek, Submit {0} documents?,{0} dokümanı gönderilsin mi?, -Submiting {0},{0} gönderiliyor, +Submitting {0},{0} gönderiliyor, Submitted Document cannot be converted back to draft. Transition row {0},Ekleyen Belge taslak geri dönüştürülemez. Geçiş satır {0}, Submitting,Tanzim Ediliyor, Subscription Notification,Abonelik Bildirimi, diff --git a/frappe/translations/uk.csv b/frappe/translations/uk.csv index e2b34b13f5..bdc06f4b1a 100644 --- a/frappe/translations/uk.csv +++ b/frappe/translations/uk.csv @@ -2398,7 +2398,7 @@ Submit after importing,Надіслати після імпортування, Submit an Issue,Подати поблему, Submit this document to confirm,Проведіть цей документ для підтвердження, Submit {0} documents?,Надіслати {0} документи?, -Submiting {0},Надсилання {0}, +Submitting {0},Надсилання {0}, Submitted Document cannot be converted back to draft. Transition row {0},Проведений документ не може бути перетворений назад у чернетку. Перехід у рядку {0}, Submitting,Проведення, Subscription Notification,Сповіщення про підписку, diff --git a/frappe/translations/ur.csv b/frappe/translations/ur.csv index db9e7d7500..21101a7975 100644 --- a/frappe/translations/ur.csv +++ b/frappe/translations/ur.csv @@ -2398,7 +2398,7 @@ Submit after importing,درآمد کرنے کے بعد جمع کروائیں, Submit an Issue,ایک مسئلہ جمع کرائیں, Submit this document to confirm,تصدیق کے لئے اس دستاویز جمع کرائیں, Submit {0} documents?,دستاویزات {0} جمع کروائیں؟, -Submiting {0},{0} جمع کرائیں, +Submitting {0},{0} جمع کرائیں, Submitted Document cannot be converted back to draft. Transition row {0},پیش دستاویز کا مسودہ تیار کرنے کے لئے واپس تبدیل نہیں کیا جا سکتا. منتقلی صف {0}, Submitting,جمع, Subscription Notification,سبسکرپشن کی اطلاع, diff --git a/frappe/translations/uz.csv b/frappe/translations/uz.csv index 13c07e6593..8c1056286b 100644 --- a/frappe/translations/uz.csv +++ b/frappe/translations/uz.csv @@ -2398,7 +2398,7 @@ Submit after importing,Importdan so'ng yuboring, Submit an Issue,Muammo berish, Submit this document to confirm,Tasdiqlash uchun ushbu hujjatni yuboring, Submit {0} documents?,{0} hujjatlarni yuborish kerakmi?, -Submiting {0},{0}, +Submitting {0},{0}, Submitted Document cannot be converted back to draft. Transition row {0},Yuborilgan hujjat taslakga o'tkazilmaydi. O'tish satri {0}, Submitting,Yuborish, Subscription Notification,Obuna bildirishnomasi, diff --git a/frappe/translations/vi.csv b/frappe/translations/vi.csv index dfa82c6c6e..a3e92fd3df 100644 --- a/frappe/translations/vi.csv +++ b/frappe/translations/vi.csv @@ -2398,7 +2398,7 @@ Submit after importing,Gửi sau khi nhập, Submit an Issue,Nộp cho quản trị, Submit this document to confirm,Gửi tài liệu này để xác nhận, Submit {0} documents?,Gửi {0} tài liệu?, -Submiting {0},Gửi {0}, +Submitting {0},Gửi {0}, Submitted Document cannot be converted back to draft. Transition row {0},Tài liệu gửi không thể được chuyển đổi trở lại dự thảo. Hàng chuyển {0}, Submitting,Trình, Subscription Notification,Thông báo đăng ký, diff --git a/frappe/translations/zh.csv b/frappe/translations/zh.csv index b8c404ce30..c82c64b5f5 100644 --- a/frappe/translations/zh.csv +++ b/frappe/translations/zh.csv @@ -2398,7 +2398,7 @@ Submit after importing,导入后提交, Submit an Issue,提交问题, Submit this document to confirm,提交该文件以确认, Submit {0} documents?,提交{0}文件?, -Submiting {0},正在提交{0}, +Submitting {0},正在提交{0}, Submitted Document cannot be converted back to draft. Transition row {0},行{0}中的已提交的文档不能转换为草稿。, Submitting,提交, Subscription Notification,订阅通知, diff --git a/frappe/translations/zh_tw.csv b/frappe/translations/zh_tw.csv index 0eb866a28f..8b2f45908f 100644 --- a/frappe/translations/zh_tw.csv +++ b/frappe/translations/zh_tw.csv @@ -2186,7 +2186,7 @@ Subject Field,主題字段, Submit after importing,導入後提交, Submit an Issue,提交問題, Submit this document to confirm,提交該文件以確認, -Submiting {0},提交{0}, +Submitting {0},提交{0}, Submitted Document cannot be converted back to draft. Transition row {0},提交的文件不能被轉換回起草。, Subscription Notification,訂閱通知, Subsidiary,副, From bafb1faa4891c9be76c3d12aa4f13cbe48ab4b88 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 18 Apr 2022 10:51:41 +0530 Subject: [PATCH 13/45] chore: Add semantic releases --- .github/workflows/release.yml | 25 +++++++++++++++++++++++++ .releaserc | 25 +++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 .releaserc diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..d7cf89f017 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,25 @@ +name: Generate Semantic Release +on: + push: + branches: + - version-13 +jobs: + release: + name: Release + runs-on: ubuntu-18.04 + steps: + - name: Checkout Entire Repository + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Setup Node.js v14 + uses: actions/setup-node@v2 + with: + node-version: 14 + - name: Setup dependencies + run: | + npm install @semantic-release/git @semantic-release/exec --no-save + - name: Create Release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: npx semantic-release \ No newline at end of file diff --git a/.releaserc b/.releaserc new file mode 100644 index 0000000000..9b18977478 --- /dev/null +++ b/.releaserc @@ -0,0 +1,25 @@ +{ + "branches": ["version-13"], + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + [ + "@semantic-release/exec", { + "prepareCmd": 'sed -ir "s/[0-9]*\.[0-9]*\.[0-9]*/${nextRelease.version}/" frappe/__init__.py' + } + ], + [ + "@semantic-release/git", { + "assets": ["frappe/__init__.py"], + "message": "chore(release): Bumped to Version ${nextRelease.version}\n\n${nextRelease.notes}" + } + ], + [ + "@semantic-release/github", { + "assets": [ + {"path": "dist/*"}, + ] + } + ] + ] +} \ No newline at end of file From 155dbb5652c0b558a25d7b1843777130ccb57d50 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Mon, 18 Apr 2022 11:54:44 +0530 Subject: [PATCH 14/45] refactor: Move checkbox style to a separate file to re-use it in print --- frappe/public/scss/common/global.scss | 97 +----------------------- frappe/public/scss/desk/global.scss | 2 - frappe/public/scss/element/checkbox.scss | 55 ++++++++++++++ frappe/public/scss/element/radio.scss | 37 +++++++++ frappe/public/scss/print.bundle.scss | 1 + 5 files changed, 96 insertions(+), 96 deletions(-) create mode 100644 frappe/public/scss/element/checkbox.scss create mode 100644 frappe/public/scss/element/radio.scss diff --git a/frappe/public/scss/common/global.scss b/frappe/public/scss/common/global.scss index 8a849ab51a..e51a7b6728 100644 --- a/frappe/public/scss/common/global.scss +++ b/frappe/public/scss/common/global.scss @@ -1,3 +1,6 @@ +@import "../element/checkbox"; +@import "../element/radio"; + html, body { height: 100%; } @@ -20,100 +23,6 @@ html, body { } } -$check-icon: url("data:image/svg+xml, "); - -input[type="radio"] { - position: relative; - width: var(--checkbox-size) !important; - height: var(--checkbox-size); - margin-right: 8px !important; - font-size: calc(var(--checkbox-size) - 1px); - background-repeat: no-repeat; - background-position: center; - cursor: pointer; - - // Reset browser behavior - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - - -webkit-print-color-adjust: exact; - color-adjust: exact; - - &:focus { - outline: none; - } - - &:before { - width: var(--checkbox-size); - height: var(--checkbox-size); - position: absolute; - content: ' '; - border: 1px solid var(--gray-400); - border-radius: 16px; - } - - &:checked::before { - background-color: var(--primary); - border-radius: 16px; - box-shadow: inset 0 0 0 2px white; - } -} - -input[type="checkbox"] { - position: relative; - width: var(--checkbox-size) !important; - height: var(--checkbox-size); - margin-right: var(--checkbox-right-margin) !important; - background-repeat: no-repeat; - background-position: center; - border: 1px solid var(--gray-400); - box-sizing: border-box; - box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.1); - border-radius: 4px; - - // Reset browser behavior - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - - -webkit-print-color-adjust: exact; - color-adjust: exact; - - .grid-static-col & { - margin-right: 0 !important; - } - - &:checked { - background-color: var(--primary); - background-image: $check-icon, var(--checkbox-gradient); - background-size: 57%, 100%; - box-shadow: none; - border: none; - } - - &:focus { - outline: none; // Prevent browser behavior - box-shadow: var(--checkbox-focus-shadow); - } - - &.disabled-deselected, &:disabled { - background-color: var(--disabled-control-bg); - box-shadow: inset 0px 1px 7px rgba(0, 0, 0, 0.1); - border: 0.5px solid var(--gray-300); - pointer-events: none; - } - - &.disabled-selected, &:disabled:checked { - background-color: var(--gray-500); - background-image: $check-icon; - background-size: 57%; - box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.1); - border: none; - pointer-events: none; - } -} - .frappe-card { @include card(); } diff --git a/frappe/public/scss/desk/global.scss b/frappe/public/scss/desk/global.scss index 7c2ae3c8b1..6a85dea6dd 100644 --- a/frappe/public/scss/desk/global.scss +++ b/frappe/public/scss/desk/global.scss @@ -2,8 +2,6 @@ html { background-color: var(--bg-color); } - - body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; diff --git a/frappe/public/scss/element/checkbox.scss b/frappe/public/scss/element/checkbox.scss new file mode 100644 index 0000000000..6e0987c00a --- /dev/null +++ b/frappe/public/scss/element/checkbox.scss @@ -0,0 +1,55 @@ +$check-icon: url("data:image/svg+xml, "); + +input[type="checkbox"] { + position: relative; + width: var(--checkbox-size) !important; + height: var(--checkbox-size); + margin-right: var(--checkbox-right-margin) !important; + background-repeat: no-repeat; + background-position: center; + border: 1px solid var(--gray-400); + box-sizing: border-box; + box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.1); + border-radius: 4px; + + // Reset browser behavior + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + + -webkit-print-color-adjust: exact; + color-adjust: exact; + + .grid-static-col & { + margin-right: 0 !important; + } + + &:checked { + background-color: var(--primary); + background-image: $check-icon, var(--checkbox-gradient); + background-size: 57%, 100%; + box-shadow: none; + border: none; + } + + &:focus { + outline: none; // Prevent browser behavior + box-shadow: var(--checkbox-focus-shadow); + } + + &.disabled-deselected, &:disabled { + background-color: var(--disabled-control-bg); + box-shadow: inset 0px 1px 7px rgba(0, 0, 0, 0.1); + border: 0.5px solid var(--gray-300); + pointer-events: none; + } + + &.disabled-selected, &:disabled:checked { + background-color: var(--gray-500); + background-image: $check-icon; + background-size: 57%; + box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.1); + border: none; + pointer-events: none; + } +} \ No newline at end of file diff --git a/frappe/public/scss/element/radio.scss b/frappe/public/scss/element/radio.scss new file mode 100644 index 0000000000..b471832a9b --- /dev/null +++ b/frappe/public/scss/element/radio.scss @@ -0,0 +1,37 @@ +input[type="radio"] { + position: relative; + width: var(--checkbox-size) !important; + height: var(--checkbox-size); + margin-right: 8px !important; + font-size: calc(var(--checkbox-size) - 1px); + background-repeat: no-repeat; + background-position: center; + cursor: pointer; + + // Reset browser behavior + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + + -webkit-print-color-adjust: exact; + color-adjust: exact; + + &:focus { + outline: none; + } + + &:before { + width: var(--checkbox-size); + height: var(--checkbox-size); + position: absolute; + content: ' '; + border: 1px solid var(--gray-400); + border-radius: 16px; + } + + &:checked::before { + background-color: var(--primary); + border-radius: 16px; + box-shadow: inset 0 0 0 2px white; + } +} \ No newline at end of file diff --git a/frappe/public/scss/print.bundle.scss b/frappe/public/scss/print.bundle.scss index 7dbaf5b496..61f56beaf8 100644 --- a/frappe/public/scss/print.bundle.scss +++ b/frappe/public/scss/print.bundle.scss @@ -1,6 +1,7 @@ @import "frappe/public/css/bootstrap.css"; @import "./common/quill"; @import "./desk/css_variables"; +@import "./element/checkbox"; // !! PDF Barcode hack !! // Workaround for rendering barcodes prior to https://github.com/frappe/frappe/pull/15307 From 6c7d867dbd4e8dc83f46a958fccc166e5850c681 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 18 Apr 2022 13:10:59 +0530 Subject: [PATCH 15/45] chore: block major releases --- .releaserc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.releaserc b/.releaserc index 9b18977478..2b3f4975db 100644 --- a/.releaserc +++ b/.releaserc @@ -1,7 +1,12 @@ { "branches": ["version-13"], "plugins": [ - "@semantic-release/commit-analyzer", + "@semantic-release/commit-analyzer", { + "preset": "angular", + "releaseRules": [ + {"breaking": true, "release": false} + ] + }, "@semantic-release/release-notes-generator", [ "@semantic-release/exec", { From 289242a68505e8611cc7c3108cc13035a2e43ec4 Mon Sep 17 00:00:00 2001 From: borgdb Date: Mon, 18 Apr 2022 11:39:19 +0300 Subject: [PATCH 16/45] fix: Update default currency & date format for Malta. (#16630) --- frappe/geo/country_info.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frappe/geo/country_info.json b/frappe/geo/country_info.json index 7ffdf0a8bf..23cadc2156 100644 --- a/frappe/geo/country_info.json +++ b/frappe/geo/country_info.json @@ -1567,12 +1567,13 @@ }, "Malta": { "code": "mt", - "currency": "MTL", + "currency": "EUR", "currency_fraction": "Cent", "currency_fraction_units": 100, - "currency_name": "Maltese Lira", + "smallest_currency_fraction_value": 0.01, "currency_symbol": "\u20ac", "number_format": "#,###.##", + "date_format": "dd/mm/yyyy", "timezones": [ "Europe/Malta" ] From 803f1fb0619c18942c547fcabe99753d1eb64f54 Mon Sep 17 00:00:00 2001 From: Himanshu Date: Mon, 18 Apr 2022 11:15:19 +0100 Subject: [PATCH 17/45] feat: add/remove fields from kanban board (#16257) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Co-authored-by: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> --- cypress/integration/kanban.js | 84 ++++++ cypress/support/commands.js | 1 + frappe/commands/utils.py | 2 +- .../doctype/kanban_board/kanban_board.json | 261 ++++------------- .../desk/doctype/kanban_board/kanban_board.py | 22 +- frappe/public/js/frappe/list/list_view.js | 17 +- .../js/frappe/views/kanban/kanban_board.js | 33 ++- .../js/frappe/views/kanban/kanban_card.html | 33 +-- .../js/frappe/views/kanban/kanban_settings.js | 264 ++++++++++++++++++ .../js/frappe/views/kanban/kanban_view.js | 22 ++ frappe/public/scss/desk/kanban.scss | 224 ++++++++------- 11 files changed, 614 insertions(+), 349 deletions(-) create mode 100644 cypress/integration/kanban.js create mode 100644 frappe/public/js/frappe/views/kanban/kanban_settings.js diff --git a/cypress/integration/kanban.js b/cypress/integration/kanban.js new file mode 100644 index 0000000000..bb9aaf3d8c --- /dev/null +++ b/cypress/integration/kanban.js @@ -0,0 +1,84 @@ +context('Kanban Board', () => { + before(() => { + cy.login(); + cy.visit('/app'); + }); + + it('Create ToDo Kanban', () => { + cy.visit('/app/todo'); + + cy.get('.page-actions .custom-btn-group button').click(); + cy.get('.page-actions .custom-btn-group ul.dropdown-menu li').contains('Kanban').click(); + + cy.fill_field('board_name', 'ToDo Kanban', 'Data'); + cy.fill_field('field_name', 'Status', 'Select'); + cy.click_modal_primary_button('Save'); + + cy.get('.title-text').should('contain', 'ToDo Kanban'); + }); + + it('Create ToDo from kanban', () => { + cy.intercept({ + method: 'POST', + url: 'api/method/frappe.client.save' + }).as('save-todo'); + + cy.click_listview_primary_button('Add ToDo'); + + cy.fill_field('description', 'Test Kanban ToDo', 'Text Editor'); + cy.get('.modal-footer .btn-primary').last().click(); + + cy.wait('@save-todo'); + }); + + it('Add and Remove fields', () => { + cy.visit('/app/todo/view/kanban/ToDo Kanban'); + + cy.intercept('POST', '/api/method/frappe.desk.doctype.kanban_board.kanban_board.save_settings').as('save-kanban'); + cy.intercept('POST', '/api/method/frappe.desk.doctype.kanban_board.kanban_board.update_order').as('update-order'); + + cy.get('.page-actions .menu-btn-group > .btn').click(); + cy.get('.page-actions .menu-btn-group .dropdown-menu li').contains('Kanban Settings').click(); + cy.get('.add-new-fields').click(); + + cy.get('.checkbox-options .checkbox').contains('ID').click(); + cy.get('.checkbox-options .checkbox').contains('Status').first().click(); + cy.get('.checkbox-options .checkbox').contains('Priority').click(); + + cy.get('.modal-footer .btn-primary').last().click(); + + cy.get('.frappe-control .label-area').contains('Show Labels').click(); + cy.click_modal_primary_button('Save'); + + cy.wait('@save-kanban'); + + cy.get('.kanban-column[data-column-value="Open"] .kanban-cards').as('open-cards'); + cy.get('@open-cards').find('.kanban-card .kanban-card-doc').first().should('contain', 'ID:'); + cy.get('@open-cards').find('.kanban-card .kanban-card-doc').first().should('contain', 'Status:'); + cy.get('@open-cards').find('.kanban-card .kanban-card-doc').first().should('contain', 'Priority:'); + + cy.get('.page-actions .menu-btn-group > .btn').click(); + cy.get('.page-actions .menu-btn-group .dropdown-menu li').contains('Kanban Settings').click(); + cy.get_open_dialog().find('.frappe-control[data-fieldname="fields_html"] div[data-label="ID"] .remove-field').click(); + + cy.wait('@update-order'); + cy.get_open_dialog().find('.frappe-control .label-area').contains('Show Labels').click(); + cy.get('.modal-footer .btn-primary').last().click(); + + cy.wait('@save-kanban'); + + cy.get('@open-cards').find('.kanban-card .kanban-card-doc').first().should('not.contain', 'ID:'); + + }); + + it('Drag todo', () => { + cy.intercept({ + method: 'POST', + url: 'api/method/frappe.desk.doctype.kanban_board.kanban_board.update_order_for_single_card' + }).as('drag-completed'); + + cy.get('.kanban-card-body:first').drag('[data-column-value="Closed"] .kanban-cards', {force: true}); + + cy.wait('@drag-completed'); + }); +}); \ No newline at end of file diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 636312376d..4847dbcf12 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -1,5 +1,6 @@ import 'cypress-file-upload'; import '@testing-library/cypress/add-commands'; +import '@4tw/cypress-drag-drop'; // *********************************************** // This example commands.js shows you how to // create various custom commands and overwrite diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index 2e27b8d6fe..bb1a8bc2d2 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -870,7 +870,7 @@ def run_ui_tests( # install cypress click.secho("Installing Cypress...", fg="yellow") frappe.commands.popen( - "yarn add cypress@^6 cypress-file-upload@^5 @testing-library/cypress@^8 @cypress/code-coverage@^3 --no-lockfile" + "yarn add cypress@^6 cypress-file-upload@^5 @4tw/cypress-drag-drop@^2 @testing-library/cypress@^8 @cypress/code-coverage@^3 --no-lockfile" ) # run for headless mode diff --git a/frappe/desk/doctype/kanban_board/kanban_board.json b/frappe/desk/doctype/kanban_board/kanban_board.json index f2e1a78d40..b1f120687c 100644 --- a/frappe/desk/doctype/kanban_board/kanban_board.json +++ b/frappe/desk/doctype/kanban_board/kanban_board.json @@ -1,267 +1,124 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, + "actions": [], "allow_rename": 1, "autoname": "field:kanban_board_name", - "beta": 0, "creation": "2016-10-19 12:26:04.809812", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "kanban_board_name", + "reference_doctype", + "field_name", + "column_break_4", + "private", + "show_labels", + "section_break_3", + "columns", + "filters", + "fields" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "kanban_board_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Kanban Board Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 0, - "set_only_once": 0, "unique": 1 }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "reference_doctype", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Reference Document Type", - "length": 0, - "no_copy": 0, "options": "DocType", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "reqd": 1 }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "field_name", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Field Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "reqd": 1 }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_3", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "columns", "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Columns", - "length": 0, - "no_copy": 0, - "options": "Kanban Board Column", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "options": "Kanban Board Column" }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "filters", - "fieldtype": "Text", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, + "fieldtype": "Code", "label": "Filters", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "options": "JSON", + "read_only": 1 }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "private", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Private", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "read_only": 1 + }, + { + "fieldname": "fields", + "fieldtype": "Code", + "label": "Fields", + "options": "JSON", + "read_only": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "show_labels", + "fieldtype": "Check", + "label": "Show Labels", + "read_only": 1 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-09-05 14:22:27.664645", + "links": [], + "modified": "2022-04-13 12:10:20.284367", "modified_by": "Administrator", "module": "Desk", "name": "Kanban Board", - "name_case": "", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, + "read": 1, + "role": "All" + }, + { + "create": 1, + "delete": 1, + "if_owner": 1, + "read": 1, + "role": "All", + "write": 1 + }, + { "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, - "role": "All", - "set_user_permissions": 0, + "role": "System Manager", "share": 1, - "submit": 0, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, + "read_only": 1, "sort_field": "modified", "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 + "states": [], + "track_changes": 1 } \ No newline at end of file diff --git a/frappe/desk/doctype/kanban_board/kanban_board.py b/frappe/desk/doctype/kanban_board/kanban_board.py index e864f68728..381f71438c 100644 --- a/frappe/desk/doctype/kanban_board/kanban_board.py +++ b/frappe/desk/doctype/kanban_board/kanban_board.py @@ -24,7 +24,7 @@ class KanbanBoard(Document): def validate_column_name(self): for column in self.columns: if not column.column_name: - frappe.msgprint(frappe._("Column Name cannot be empty"), raise_exception=True) + frappe.msgprint(_("Column Name cannot be empty"), raise_exception=True) def get_permission_query_conditions(user): @@ -92,7 +92,6 @@ def update_order(board_name, order): updated_cards = [] for col_name, cards in order_dict.items(): - order_list = [] for card in cards: column = frappe.get_value(doctype, {"name": card}, fieldname) if column != col_name: @@ -246,3 +245,22 @@ def set_indicator(board_name, column_name, indicator): board.save() return board + + +@frappe.whitelist() +def save_settings(board_name: str, settings: str) -> Document: + settings = json.loads(settings) + doc = frappe.get_doc("Kanban Board", board_name) + + fields = settings["fields"] + if not isinstance(fields, str): + fields = json.dumps(fields) + + doc.fields = fields + doc.show_labels = settings["show_labels"] + doc.save() + + resp = doc.as_dict() + resp["fields"] = frappe.parse_json(resp["fields"]) + + return resp diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 069f353368..35e387b8d8 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -1580,15 +1580,22 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { } if (frappe.user.has_role("System Manager")) { - items.push({ - label: __("List Settings", null, "Button in list view menu"), - action: () => this.show_list_settings(), - standard: true, - }); + if (this.get_view_settings) { + items.push(this.get_view_settings()); + } } + return items; } + get_view_settings() { + return { + label: __("List Settings", null, "Button in list view menu"), + action: () => this.show_list_settings(), + standard: true, + }; + } + show_list_settings() { frappe.model.with_doctype(this.doctype, () => { new ListSettings({ diff --git a/frappe/public/js/frappe/views/kanban/kanban_board.js b/frappe/public/js/frappe/views/kanban/kanban_board.js index 58d58b27fc..64e90f5326 100644 --- a/frappe/public/js/frappe/views/kanban/kanban_board.js +++ b/frappe/public/js/frappe/views/kanban/kanban_board.js @@ -630,8 +630,6 @@ frappe.provide("frappe.views"); if(!card) return; make_dom(); render_card_meta(); - add_task_link(); - // edit_card_title(); } function make_dom() { @@ -640,12 +638,35 @@ frappe.provide("frappe.views"); title: frappe.utils.html2text(card.title), disable_click: card._disable_click ? 'disable-click' : '', creation: card.creation, + doc_content: get_doc_content(card), image_url: cur_list.get_image_url(card), + form_link: frappe.utils.get_form_link(card.doctype, card.name) }; + self.$card = $(frappe.render_template('kanban_card', opts)) .appendTo(wrapper); } + function get_doc_content(card) { + let fields = []; + for (let field_name of cur_list.board.fields) { + let field = ( + frappe.meta.get_docfield(card.doctype, field_name, card.name) + || frappe.model.get_std_field(field_name) + ); + let label = cur_list.board.show_labels ? `${__(field.label)}: ` : ''; + let value = frappe.format(card.doc[field_name], field); + fields.push(` +
+ ${label} + ${value} +
+ `); + } + + return fields.join(""); + } + function get_tags_html(card) { return card.tags ? `
@@ -688,12 +709,6 @@ frappe.provide("frappe.views"); .find('.kanban-assignments').append($assignees_group); } - function add_task_link() { - let task_link = frappe.utils.get_form_link(card.doctype, card.name); - self.$card.find('.kanban-card-redirect') - .attr('href', task_link); - } - function get_assignees_group() { return frappe.avatar_group(card.assigned_list, 3, { css_class: 'avatar avatar-small', @@ -744,7 +759,7 @@ frappe.provide("frappe.views"); assigned_list: card.assigned_list || assigned_list, comment_count: card.comment_count || comment_count, color: card.color || null, - doc: doc + doc: doc || card }; } diff --git a/frappe/public/js/frappe/views/kanban/kanban_card.html b/frappe/public/js/frappe/views/kanban/kanban_card.html index 88cf366ccc..ed8e4cc6ff 100644 --- a/frappe/public/js/frappe/views/kanban/kanban_card.html +++ b/frappe/public/js/frappe/views/kanban/kanban_card.html @@ -1,23 +1,24 @@
- -
- {% if(image_url) { %} -
- {{title}} -
- {% } %} -
diff --git a/frappe/public/js/frappe/views/kanban/kanban_settings.js b/frappe/public/js/frappe/views/kanban/kanban_settings.js new file mode 100644 index 0000000000..b41922e544 --- /dev/null +++ b/frappe/public/js/frappe/views/kanban/kanban_settings.js @@ -0,0 +1,264 @@ +export default class KanbanSettings { + constructor({ kanbanview, doctype, meta, settings }) { + if (!doctype) { + frappe.throw(__("DocType required")); + } + + this.kanbanview = kanbanview; + this.doctype = doctype; + this.meta = meta; + this.settings = settings; + this.dialog = null; + this.fields = this.settings && this.settings.fields; + + frappe.model.with_doctype("List View Settings", () => { + this.make(); + this.get_fields(); + this.setup_fields(); + this.setup_remove_fields(); + this.add_new_fields(); + this.show_dialog(); + }); + } + + make() { + this.dialog = new frappe.ui.Dialog({ + title: __("{0} Settings", [__(this.doctype)]), + fields: [ + { + fieldname: "show_labels", + label: __("Show Labels"), + fieldtype: "Check", + }, + { + fieldname: "fields_html", + fieldtype: "HTML" + }, + { + fieldname: "fields", + fieldtype: "Code", + hidden: 1 + } + ] + }); + this.dialog.set_values(this.settings); + this.dialog.set_primary_action(__("Save"), () => { + frappe.show_alert({ + message: __("Saving"), + indicator: "green" + }); + + frappe.call({ + method: + "frappe.desk.doctype.kanban_board.kanban_board.save_settings", + args: { + board_name: this.settings.name, + settings: this.dialog.get_values() + }, + callback: r => { + this.kanbanview.board = r.message; + this.kanbanview.render(); + this.dialog.hide(); + } + }); + }); + } + + refresh() { + this.setup_fields(); + this.add_new_fields(); + this.setup_remove_fields(); + } + + show_dialog() { + if (!this.settings.fields) { + this.update_fields(); + } + + this.dialog.show(); + } + + setup_fields() { + const fields_html = this.dialog.get_field("fields_html"); + const wrapper = fields_html.$wrapper[0]; + let fields = ""; + + for (let fieldname of this.fields) { + let field = this.get_docfield(fieldname); + + fields += ` +
+ +
+
+ ${frappe.utils.icon("drag", "xs", "", "", "sortable-handle")} +
+
+ ${__(field.label)} +
+ +
+
`; + } + + fields_html.html(` +
+
+ +
+
+ ${fields} +
+

+ + + Add / Remove Fields + +

+
+ `); + + new Sortable( + wrapper.getElementsByClassName("control-input-wrapper")[0], + { + handle: ".sortable-handle", + draggable: ".sortable", + onUpdate: params => { + this.fields.splice( + params.newIndex, + 0, + this.fields.splice(params.oldIndex, 1)[0] + ); + this.dialog.set_value( + "fields", + JSON.stringify(this.fields) + ); + this.refresh(); + } + } + ); + } + + add_new_fields() { + let add_new_fields = this.get_dialog_fields_wrapper().getElementsByClassName( + "add-new-fields" + )[0]; + add_new_fields.onclick = () => this.show_column_selector(); + } + + setup_remove_fields() { + let remove_fields = this.get_dialog_fields_wrapper().getElementsByClassName( + "remove-field" + ); + + for (let idx = 0; idx < remove_fields.length; idx++) { + remove_fields.item(idx).onclick = () => + this.remove_fields( + remove_fields.item(idx).getAttribute("data-fieldname") + ); + } + } + + get_dialog_fields_wrapper() { + return this.dialog.get_field("fields_html").$wrapper[0]; + } + + remove_fields(fieldname) { + this.fields = this.fields.filter(field => field !== fieldname); + this.dialog.set_value("fields", JSON.stringify(this.fields)); + this.refresh(); + } + + update_fields() { + const wrapper = this.dialog.get_field("fields_html").$wrapper[0]; + let fields_order = wrapper.getElementsByClassName("fields_order"); + this.fields = []; + + for (let idx = 0; idx < fields_order.length; idx++) { + this.fields.push( + fields_order.item(idx).getAttribute("data-fieldname") + ); + } + + this.dialog.set_value("fields", JSON.stringify(this.fields)); + } + + show_column_selector() { + let dialog = new frappe.ui.Dialog({ + title: __("{0} Fields", [__(this.doctype)]), + fields: [ + { + label: __("Select Fields"), + fieldtype: "MultiCheck", + fieldname: "fields", + options: this.get_multiselect_fields(), + columns: 2 + } + ] + }); + dialog.set_primary_action(__("Save"), () => { + this.fields = dialog.get_values().fields || []; + this.dialog.set_value("fields", JSON.stringify(this.fields)); + this.refresh(); + dialog.hide(); + }); + dialog.show(); + } + + get_fields() { + this.fields = this.settings.fields; + this.fields.uniqBy(f => f.fieldname); + } + + get_docfield(field_name) { + return ( + frappe.meta.get_docfield(this.doctype, field_name) || + frappe.model.get_std_field(field_name) + ); + } + + get_multiselect_fields() { + const ignore_fields = [ + "idx", + "lft", + "rgt", + "old_parent", + "_user_tags", + "_liked_by", + "_comments", + "_assign", + this.meta.title_field || "name" + ]; + + const ignore_fieldtypes = [ + "Attach Image", + "Text Editor", + "HTML Editor", + "Code", + "Color", + ...frappe.model.no_value_type + ]; + + return frappe.model.std_fields + .concat(this.kanbanview.get_fields_in_list_view()) + .filter( + field => + !ignore_fields.includes(field.fieldname) && + !ignore_fieldtypes.includes(field.fieldtype) + ) + .map(field => { + return { + label: __(field.label), + value: field.fieldname, + checked: this.fields.includes(field.fieldname) + }; + }); + } +} diff --git a/frappe/public/js/frappe/views/kanban/kanban_view.js b/frappe/public/js/frappe/views/kanban/kanban_view.js index 3bf3a16189..b546365fd9 100644 --- a/frappe/public/js/frappe/views/kanban/kanban_view.js +++ b/frappe/public/js/frappe/views/kanban/kanban_view.js @@ -1,3 +1,5 @@ +import KanbanSettings from "./kanban_settings"; + frappe.provide('frappe.views'); frappe.views.KanbanView = class KanbanView extends frappe.views.ListView { @@ -57,6 +59,7 @@ frappe.views.KanbanView = class KanbanView extends frappe.views.ListView { .then(board => { this.board = board; this.board.filters_array = JSON.parse(this.board.filters || '[]'); + this.board.fields = JSON.parse(this.board.fields || '[]'); this.filters = this.board.filters_array; }); } @@ -187,6 +190,25 @@ frappe.views.KanbanView = class KanbanView extends frappe.views.ListView { }; } + get_view_settings() { + return { + label: __("Kanban Settings", null, "Button in kanban view menu"), + action: () => this.show_kanban_settings(), + standard: true, + }; + } + + show_kanban_settings() { + frappe.model.with_doctype(this.doctype, () => { + new KanbanSettings({ + kanbanview: this, + doctype: this.doctype, + settings: this.board, + meta: frappe.get_meta(this.doctype) + }); + }); + } + get required_libs() { return [ 'assets/frappe/js/lib/fluxify.min.js', diff --git a/frappe/public/scss/desk/kanban.scss b/frappe/public/scss/desk/kanban.scss index 41bd0b2859..8f286b7b35 100644 --- a/frappe/public/scss/desk/kanban.scss +++ b/frappe/public/scss/desk/kanban.scss @@ -139,6 +139,7 @@ } .kanban-cards { + height: 100%; max-height: calc(100vh - 250px); margin: -5px; padding: 5px; @@ -149,39 +150,120 @@ &::-webkit-scrollbar { display: none; } - } - .kanban-card { - @include flex(flex, space-between, null, column); - margin-top: var(--margin-sm); - min-height: 100px; - @include card( - $padding: 0, - $background-color: var(--kanban-card-bg) - ); - .kanban-card-body { - padding: var(--padding-sm); + .kanban-card-wrapper { + position: relative; + display: block; + + &:last-child .kanban-card { + margin-bottom: var(--margin-xl); + } + .kanban-card { + @include flex(flex, space-between, null, column); + margin-top: var(--margin-sm); + min-height: 100px; + @include card( + $padding: 0, + $background-color: var(--kanban-card-bg) + ); + + .kanban-image { + height: 125px; + + img { + border-radius: var(--border-radius) var(--border-radius) 0 0; + object-position: top; + object-fit: cover; + margin: 0 auto; + height: 100%; + width: 100%; + min-width: 100%; + color: transparent; + position: relative; + } + + @include broken-img( + $height: 125px, + $top: -4px, + ) + } + + .kanban-card-body { + cursor: grab; + padding: var(--padding-sm); + + .kanban-title-area { + margin-bottom: 12px; + max-width: 90%; + font-size: var(--text-md); + font-weight: 500; + + .kanban-card-doc { + .text-muted div { + display: inline; + } + } + + .kanban-card-creation { + font-size: var(--text-md); + color: var(--text-muted); + margin-top: var(--margin-xs); + } + } + + .kanban-card-meta { + + .list-comment-count { + width: 30px; + } + + .like-action:not(.liked) { + .icon use { + stroke: var(--text-muted); + } + } + + .kanban-tags { + font-size: var(--text-sm); + margin-bottom: 8px; + + .tag-pill { + border-radius: 100px; + height: 22px; + width: auto; + padding: 2px 8px; + margin-bottom: var(--margin-xs); + margin-right: var(--margin-xs); + } + } + + .kanban-assignments { + display: flex; + float: right; + + .avatar { + cursor: default; + width: 22px; + height: 22px; + } + + .avatar-action { + width: 22px; + height: 22px; + + .icon { + width: 12px; + height: 12px; + } + } + } + } + } + } } } } - .kanban-card-wrapper { - position: relative; - - .kanban-card-redirect { - display: block; - - &:hover, - &:focus { - text-decoration: none; - } - } - - &:last-child .kanban-card { - margin-bottom: var(--margin-xl); - } - } - .kanban-card:hover, .new-card-area, .edit-card-area { @@ -189,7 +271,6 @@ } .kanban-card-wrapper:hover { - cursor: pointer; text-decoration: none; .kanban-card-edit { @@ -197,43 +278,6 @@ } } - .kanban-title-area { - margin-bottom: 12px; - - .kanban-card-title { - max-width: 90%; - font-size: var(--text-md); - font-weight: 500; - } - - .kanban-card-creation { - font-size: var(--text-md); - color: var(--text-muted); - margin-top: var(--margin-xs); - } - } - - .kanban-image { - height: 125px; - - img { - border-radius: var(--border-radius) var(--border-radius) 0 0; - object-position: top; - object-fit: cover; - margin: 0 auto; - height: 100%; - width: 100%; - min-width: 100%; - color: transparent; - position: relative; - } - - @include broken-img( - $height: 125px, - $top: -4px, - ) - } - .kanban-card-edit { position: absolute; right: 10px; @@ -291,54 +335,6 @@ } } - .kanban-card-meta { - - .list-comment-count { - width: 30px; - } - - .like-action:not(.liked) { - .icon use { - stroke: var(--text-muted); - } - } - - .kanban-tags { - font-size: var(--text-sm); - margin-bottom: 8px; - - .tag-pill { - border-radius: 100px; - height: 22px; - width: auto; - padding: 2px 8px; - margin-bottom: var(--margin-xs); - margin-right: var(--margin-xs); - } - } - - .kanban-assignments { - display: flex; - float: right; - - .avatar { - cursor: default; - width: 22px; - height: 22px; - } - - .avatar-action { - width: 22px; - height: 22px; - - .icon { - width: 12px; - height: 12px; - } - } - } - } - .kanban-empty-state { width: 100%; line-height: 400px; From 17aeec04c85168e84d94dc8205134a959f74cedd Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 18 Apr 2022 16:45:39 +0530 Subject: [PATCH 18/45] chore: do not publish any assets --- .releaserc | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.releaserc b/.releaserc index 2b3f4975db..530a6c0767 100644 --- a/.releaserc +++ b/.releaserc @@ -19,12 +19,6 @@ "message": "chore(release): Bumped to Version ${nextRelease.version}\n\n${nextRelease.notes}" } ], - [ - "@semantic-release/github", { - "assets": [ - {"path": "dist/*"}, - ] - } - ] + "@semantic-release/github" ] } \ No newline at end of file From bf21d2fe2a3dee9ec68556a05ff795c2ea5c1779 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 18 Apr 2022 16:46:14 +0530 Subject: [PATCH 19/45] chore: use ubuntu latest --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d7cf89f017..52fc595c96 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,7 +6,7 @@ on: jobs: release: name: Release - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest steps: - name: Checkout Entire Repository uses: actions/checkout@v2 From 57a55e4225821c6b9034b8633fe86eac624c002b Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 18 Apr 2022 15:19:25 +0530 Subject: [PATCH 20/45] feat(minor): Add document reference to Error Log and doc.log_error --- frappe/__init__.py | 29 ++- .../doctype/auto_repeat/auto_repeat.py | 2 +- .../doctype/communication/communication.py | 3 +- frappe/core/doctype/communication/email.py | 2 +- .../core/doctype/data_import/data_import.py | 2 +- frappe/core/doctype/error_log/error_log.json | 203 ++++++------------ frappe/core/doctype/file/file.py | 4 +- .../prepared_report/prepared_report.py | 2 +- frappe/core/doctype/user/user.py | 2 +- frappe/desk/desktop.py | 8 +- .../notification_log/notification_log.py | 2 +- .../system_console/system_console.json | 8 + frappe/desk/query_report.py | 2 +- .../auto_email_report/auto_email_report.py | 3 +- .../doctype/email_account/email_account.py | 9 +- .../email/doctype/email_queue/email_queue.py | 15 +- frappe/email/doctype/newsletter/newsletter.py | 10 +- .../doctype/notification/notification.py | 8 +- frappe/email/queue.py | 2 +- frappe/email/receive.py | 4 +- .../doctype/event_consumer/event_consumer.py | 2 +- frappe/model/document.py | 8 + frappe/model/workflow.py | 5 +- .../energy_point_rule/energy_point_rule.py | 4 + frappe/utils/print_format.py | 6 +- .../doctype/web_form/templates/web_form.html | 2 + 26 files changed, 152 insertions(+), 195 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 10c8afbf23..07a580944f 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -2068,26 +2068,33 @@ def logger( ) -def log_error(message=None, title=_("Error")): - """Log error to Error Log""" +def log_error(title=None, message=None, reference_doctype=None, reference_name=None): + '''Log error to Error Log''' - # AI ALERT: + # Parameter ALERT: # the title and message may be swapped # the better API for this is log_error(title, message), and used in many cases this way # this hack tries to be smart about whats a title (single line ;-)) and fixes it if message: - if "\n" in title: - error, title = title, message + if '\n' in title: # traceback sent as title + traceback, title = title, message else: - error = message - else: - error = get_traceback() + traceback = message - return get_doc(dict(doctype="Error Log", error=as_unicode(error), method=title)).insert( - ignore_permissions=True - ) + if not traceback: + traceback = get_traceback() + if not title: + title = 'Error' + + return get_doc(dict( + doctype='Error Log', + error=as_unicode(traceback), + method=title, + reference_doctype=reference_doctype, + reference_name=reference_name + )).insert(ignore_permissions=True) def get_desk_link(doctype, name): html = ( diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.py b/frappe/automation/doctype/auto_repeat/auto_repeat.py index 5ff9e9f3ab..4b2f83f4b6 100644 --- a/frappe/automation/doctype/auto_repeat/auto_repeat.py +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.py @@ -189,7 +189,7 @@ class AutoRepeat(Document): if self.notify_by_email and self.recipients: self.send_notification(new_doc) except Exception: - error_log = frappe.log_error(frappe.get_traceback(), _("Auto Repeat Document Creation Failure")) + error_log = self.log_error('Auto repeat failed') self.disable_auto_repeat() diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index 409c4c0956..eeddb7c7a1 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -450,8 +450,7 @@ def get_contacts(email_strings: List[str], auto_create_contact=False) -> List[st contact.insert(ignore_permissions=True) contact_name = contact.name except Exception: - traceback = frappe.get_traceback() - frappe.log_error(traceback) + contact.log_error('Unable to add contact') if contact_name: contacts.append(contact_name) diff --git a/frappe/core/doctype/communication/email.py b/frappe/core/doctype/communication/email.py index 5737572194..d47b15d360 100755 --- a/frappe/core/doctype/communication/email.py +++ b/frappe/core/doctype/communication/email.py @@ -248,7 +248,7 @@ def mark_email_as_seen(name: str = None): frappe.db.commit() # nosemgrep: this will be called in a GET request except Exception: - frappe.log_error(frappe.get_traceback()) + frappe.log_error('Unable to mark as seen', None, 'Communication', name) finally: frappe.response.update( diff --git a/frappe/core/doctype/data_import/data_import.py b/frappe/core/doctype/data_import/data_import.py index 295f7e79ba..3693ea53a4 100644 --- a/frappe/core/doctype/data_import/data_import.py +++ b/frappe/core/doctype/data_import/data_import.py @@ -113,7 +113,7 @@ def start_import(data_import): except Exception: frappe.db.rollback() data_import.db_set("status", "Error") - frappe.log_error(title=data_import.name) + data_import.log_error('Data import failed') finally: frappe.flags.in_import = False diff --git a/frappe/core/doctype/error_log/error_log.json b/frappe/core/doctype/error_log/error_log.json index 35ca3ceeef..e643bbb090 100644 --- a/frappe/core/doctype/error_log/error_log.json +++ b/frappe/core/doctype/error_log/error_log.json @@ -1,148 +1,75 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2013-01-16 13:09:40", - "custom": 0, - "description": "Log of Scheduler Errors", - "docstatus": 0, - "doctype": "DocType", - "document_type": "System", - "editable_grid": 0, - "engine": "MyISAM", + "actions": [], + "creation": "2013-01-16 13:09:40", + "doctype": "DocType", + "document_type": "System", + "engine": "MyISAM", + "field_order": [ + "seen", + "method", + "error", + "reference_doctype", + "reference_name" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "seen", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Seen", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "seen", + "fieldtype": "Check", + "hidden": 1, + "label": "Seen" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "method", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "method", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Title", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "error", - "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Error", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "error", + "fieldtype": "Code", + "label": "Error", + "read_only": 1 + }, + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Reference DocType", + "options": "DocType", + "search_index": 1 + }, + { + "fieldname": "reference_name", + "fieldtype": "Data", + "label": "Reference Name" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-warning-sign", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2021-10-25 12:21:44.292471", - "modified_by": "Administrator", - "module": "Core", - "name": "Error Log", - "owner": "Administrator", + ], + "icon": "fa fa-warning-sign", + "idx": 1, + "links": [], + "modified": "2022-04-18 14:51:30.604304", + "modified_by": "Administrator", + "module": "Core", + "name": "Error Log", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_order": "ASC", - "track_seen": 0 -} + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "ASC", + "states": [] +} \ No newline at end of file diff --git a/frappe/core/doctype/file/file.py b/frappe/core/doctype/file/file.py index d8b45cf043..3547a03832 100755 --- a/frappe/core/doctype/file/file.py +++ b/frappe/core/doctype/file/file.py @@ -1043,7 +1043,7 @@ def attach_files_to_document(doc, event): ): return - frappe.get_doc( + file_doc = frappe.get_doc( doctype="File", file_url=value, attached_to_name=doc.name, @@ -1052,4 +1052,4 @@ def attach_files_to_document(doc, event): folder="Home/Attachments", ).insert() except Exception: - frappe.log_error(title=_("Error Attaching File")) + file_doc.log_error("Error Attaching File") diff --git a/frappe/core/doctype/prepared_report/prepared_report.py b/frappe/core/doctype/prepared_report/prepared_report.py index c3122fe52f..dbdba19fbe 100644 --- a/frappe/core/doctype/prepared_report/prepared_report.py +++ b/frappe/core/doctype/prepared_report/prepared_report.py @@ -47,7 +47,7 @@ def run_background(prepared_report): instance.save(ignore_permissions=True) except Exception: - frappe.log_error(frappe.get_traceback()) + report.log_error('Prepared report failed') instance = frappe.get_doc("Prepared Report", prepared_report) instance.status = "Error" instance.error_message = frappe.get_traceback() diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index c90cbf1fce..ec7374d72a 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -265,7 +265,7 @@ class User(Document): except frappe.OutgoingEmailError: # email server not set, don't send email - frappe.log_error(frappe.get_traceback()) + self.log_error('Unable to send new password notification') @Document.hook def validate_reset_password(self): diff --git a/frappe/desk/desktop.py b/frappe/desk/desktop.py index 385151f754..ae34cf3df2 100644 --- a/frappe/desk/desktop.py +++ b/frappe/desk/desktop.py @@ -338,7 +338,7 @@ def get_desktop_page(page): "onboardings": workspace.onboardings, } except DoesNotExistError: - frappe.log_error(frappe.get_traceback()) + frappe.log_error('Workspace Missing') return {} @@ -469,10 +469,8 @@ def save_new_widget(doc, page, blocks, new_widgets): page: {0} config: {1} exception: {2} - """.format( - page, json_config, e - ) - frappe.log_error(log, _("Could not save customization")) + """.format(page, json_config, e) + doc.log_error("Could not save customization", log) return False return True diff --git a/frappe/desk/doctype/notification_log/notification_log.py b/frappe/desk/doctype/notification_log/notification_log.py index 1a466ea78b..011f3e22ff 100644 --- a/frappe/desk/doctype/notification_log/notification_log.py +++ b/frappe/desk/doctype/notification_log/notification_log.py @@ -20,7 +20,7 @@ class NotificationLog(Document): try: send_notification_email(self) except frappe.OutgoingEmailError: - frappe.log_error(message=frappe.get_traceback(), title=_("Failed to send notification email")) + self.log_error(_("Failed to send notification email")) def get_permission_query_conditions(for_user): diff --git a/frappe/desk/doctype/system_console/system_console.json b/frappe/desk/doctype/system_console/system_console.json index c92b2005ed..bbb815e30e 100644 --- a/frappe/desk/doctype/system_console/system_console.json +++ b/frappe/desk/doctype/system_console/system_console.json @@ -1,7 +1,11 @@ { "actions": [ { +<<<<<<< HEAD "action": "app/console-log", +======= + "action": "/app/console-log", +>>>>>>> 6d82805831 (feat(minor): Add document reference to Error Log and doc.log_error) "action_type": "Route", "label": "Logs" }, @@ -86,7 +90,11 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], +<<<<<<< HEAD "modified": "2022-04-09 16:35:32.345542", +======= + "modified": "2022-04-15 14:15:58.398590", +>>>>>>> 6d82805831 (feat(minor): Add document reference to Error Log and doc.log_error) "modified_by": "Administrator", "module": "Desk", "name": "System Console", diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py index 7de8ccabbf..d1f63a6199 100644 --- a/frappe/desk/query_report.py +++ b/frappe/desk/query_report.py @@ -314,7 +314,7 @@ def get_prepared_report_result(report, filters, dn="", user=None): latest_report_data = {"columns": columns, "result": data} except Exception: - frappe.log_error(frappe.get_traceback()) + doc.log_error(frappe.get_traceback()) frappe.delete_doc("Prepared Report", doc.name) frappe.db.commit() doc = None diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.py b/frappe/email/doctype/auto_email_report/auto_email_report.py index f4fdcf4275..108cc46792 100644 --- a/frappe/email/doctype/auto_email_report/auto_email_report.py +++ b/frappe/email/doctype/auto_email_report/auto_email_report.py @@ -255,8 +255,7 @@ def send_daily(): try: auto_email_report.send() except Exception as e: - frappe.log_error(e, _("Failed to send {0} Auto Email Report").format(auto_email_report.name)) - + auto_email_report.log_error('Failed to send {0} Auto Email Report'.format(auto_email_report.name)) def send_monthly(): """Check reports to be sent monthly""" diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index e60be0d965..65586de31d 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -473,7 +473,7 @@ class EmailAccount(Document): frappe.db.rollback() except Exception: frappe.db.rollback() - frappe.log_error(title="EmailAccount.receive") + self.log_error(title="EmailAccount.receive") if self.use_imap: self.handle_bad_emails(mail.uid, mail.raw_message, frappe.get_traceback()) exceptions.append(frappe.get_traceback()) @@ -521,7 +521,7 @@ class EmailAccount(Document): # close connection to mailserver email_server.logout() except Exception: - frappe.log_error(title=_("Error while connecting to email account {0}").format(self.name)) + self.log_error(title=_("Error while connecting to email account {0}").format(self.name)) return [] return mails @@ -667,7 +667,7 @@ class EmailAccount(Document): try: email_server = self.get_incoming_server(in_receive=True) except Exception: - frappe.log_error(title=_("Error while connecting to email account {0}").format(self.name)) + self.log_error(title=_("Error while connecting to email account {0}").format(self.name)) if not email_server: return @@ -679,8 +679,7 @@ class EmailAccount(Document): message = safe_encode(message) email_server.imap.append("Sent", "\\Seen", imaplib.Time2Internaldate(time.time()), message) except Exception: - frappe.log_error(title="EmailAccount.append_email_to_sent_folder") - + self.log_error(title="EmailAccount.append_email_to_sent_folder") @frappe.whitelist() def get_append_to( diff --git a/frappe/email/doctype/email_queue/email_queue.py b/frappe/email/doctype/email_queue/email_queue.py index 38577eeb97..b3e52ef1e2 100644 --- a/frappe/email/doctype/email_queue/email_queue.py +++ b/frappe/email/doctype/email_queue/email_queue.py @@ -198,10 +198,7 @@ class SendMailContext: traceback_string = "".join(traceback.format_tb(exc_tb)) traceback_string += f"\n Queue Name: {self.queue_doc.name}" - if self.is_background_task: - frappe.log_error(title="frappe.email.queue.flush", message=traceback_string) - else: - frappe.log_error(message=traceback_string) + self.queue_doc.log_error('Email sending failed', traceback_string) @property def smtp_session(self): @@ -625,12 +622,10 @@ class QueueBuilder: mail_to_string = cstr(mail.as_string()) except frappe.InvalidEmailAddressError: # bad Email Address - don't add to queue - frappe.log_error( - "Invalid Email ID Sender: {0}, Recipients: {1}, \nTraceback: {2} ".format( - self.sender, ", ".join(self.final_recipients()), traceback.format_exc() - ), - "Email Not Sent", - ) + self.log_error( + title = 'Invalid email address', + message = 'Invalid email address Sender: {0}, Recipients: {1}, \nTraceback: {2} ' + .format(self.sender, ', '.join(self.final_recipients()), traceback.format_exc())) return d = { diff --git a/frappe/email/doctype/newsletter/newsletter.py b/frappe/email/doctype/newsletter/newsletter.py index 45a4539866..e3726ed5a6 100644 --- a/frappe/email/doctype/newsletter/newsletter.py +++ b/frappe/email/doctype/newsletter/newsletter.py @@ -329,19 +329,25 @@ def send_scheduled_email(): pluck="name", ) - for newsletter in scheduled_newsletter: + for newsletter_name in scheduled_newsletter: try: - frappe.get_doc("Newsletter", newsletter).queue_all() + newsletter = frappe.get_doc("Newsletter", newsletter_name) + newsletter.queue_all() except Exception: frappe.db.rollback() # wasn't able to send emails :( +<<<<<<< HEAD frappe.db.set_value("Newsletter", newsletter, "email_sent", 0) message = ( f"Newsletter {newsletter} failed to send" "\n\n" f"Traceback: {frappe.get_traceback()}" ) frappe.log_error(title="Send Newsletter", message=message) +======= + frappe.db.set_value("Newsletter", newsletter_name, "email_sent", 0) + newsletter.log_error('Failed to send newsletter') +>>>>>>> 6d82805831 (feat(minor): Add document reference to Error Log and doc.log_error) if not frappe.flags.in_test: frappe.db.commit() diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py index 5c27eb95eb..e868c0ceb6 100644 --- a/frappe/email/doctype/notification/notification.py +++ b/frappe/email/doctype/notification/notification.py @@ -141,7 +141,7 @@ def get_context(context): self.create_system_notification(doc, context) except: - frappe.log_error(title="Failed to send notification", message=frappe.get_traceback()) + self.log_error('Failed to send Notification') if self.set_property_after_alert: allow_update = True @@ -168,7 +168,7 @@ def get_context(context): doc.save(ignore_permissions=True) doc.flags.in_notification_update = False except Exception: - frappe.log_error(title="Document update failed", message=frappe.get_traceback()) + self.log_error('Document update failed') def create_system_notification(self, doc, context): subject = self.subject @@ -432,8 +432,8 @@ def evaluate_alert(doc, alert, event): if event == "Value Change" and not doc.is_new(): if not frappe.db.has_column(doc.doctype, alert.value_changed): - alert.db_set("enabled", 0) - frappe.log_error("Notification {0} has been disabled due to missing field".format(alert.name)) + alert.db_set('enabled', 0) + alert.log_error('Notification {0} has been disabled due to missing field'.format(alert.name)) return doc_before_save = doc.get_doc_before_save() diff --git a/frappe/email/queue.py b/frappe/email/queue.py index b0a3b0583b..ac873182a7 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -170,7 +170,7 @@ def flush(from_test=False): is_background_task = not from_test func(email_queue_name=row.name, is_background_task=is_background_task) except Exception: - frappe.log_error() + frappe.get_doc('Email Queue', row.name).log_error() def get_queue(): diff --git a/frappe/email/receive.py b/frappe/email/receive.py index 4a6db65a84..80c413faa1 100644 --- a/frappe/email/receive.py +++ b/frappe/email/receive.py @@ -123,7 +123,7 @@ class EmailServer: except _socket.error: # log performs rollback and logs error in Error Log - frappe.log_error("receive.connect_pop") + self.log_error("POP: Unable to connect") # Invalid mail server -- due to refusing connection frappe.msgprint(_("Invalid Mail Server. Please rectify and try again.")) @@ -306,7 +306,7 @@ class EmailServer: else: # log performs rollback and logs error in Error Log - frappe.log_error("receive.get_messages", self.make_error_msg(msg_num, incoming_mail)) + self.log_error("Unable to fetch email", self.make_error_msg(msg_num, incoming_mail)) self.errors = True frappe.db.rollback() diff --git a/frappe/event_streaming/doctype/event_consumer/event_consumer.py b/frappe/event_streaming/doctype/event_consumer/event_consumer.py index 287a1fca03..dd3d2ec74c 100644 --- a/frappe/event_streaming/doctype/event_consumer/event_consumer.py +++ b/frappe/event_streaming/doctype/event_consumer/event_consumer.py @@ -213,5 +213,5 @@ def has_consumer_access(consumer, update_log): else: return frappe.safe_eval(condition, frappe._dict(doc=doc)) except Exception as e: - frappe.log_error(title="has_consumer_access error", message=e) + consumer.log_error('has_consumer_access error') return False diff --git a/frappe/model/document.py b/frappe/model/document.py index 58c406607d..a7b59ae749 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -1362,6 +1362,14 @@ class Document(BaseDocument): ).insert(ignore_permissions=True) frappe.local.flags.commit = True + def log_error(self, title=None, message=None): + '''Helper function to create an Error Log''' + return frappe.log_error( + message = message, + title = title, + reference_doctype = self.doctype, + reference_name = self.name) + def get_signature(self): """Returns signature (hash) for private URL.""" return hashlib.sha224(get_datetime_str(self.creation).encode()).hexdigest() diff --git a/frappe/model/workflow.py b/frappe/model/workflow.py index 0edffaf2fb..092ad4f764 100644 --- a/frappe/model/workflow.py +++ b/frappe/model/workflow.py @@ -254,8 +254,9 @@ def bulk_workflow_approval(docnames, doctype, action): frappe.db.rollback() frappe.log_error( - frappe.get_traceback(), - "Workflow {0} threw an error for {1} {2}".format(action, doctype, docname), + title = "Workflow {0} threw an error for {1} {2}".format(action, doctype, docname), + reference_doctype = 'Workflow', + reference_name = action ) finally: if not message_dict: diff --git a/frappe/social/doctype/energy_point_rule/energy_point_rule.py b/frappe/social/doctype/energy_point_rule/energy_point_rule.py index a36581ec4c..3767a9564e 100644 --- a/frappe/social/doctype/energy_point_rule/energy_point_rule.py +++ b/frappe/social/doctype/energy_point_rule/energy_point_rule.py @@ -57,7 +57,11 @@ class EnergyPointRule(Document): self.apply_only_once, ) except Exception as e: +<<<<<<< HEAD frappe.log_error(frappe.get_traceback(), "apply_energy_point") +======= + self.log_error('Energy points failed') +>>>>>>> 6d82805831 (feat(minor): Add document reference to Error Log and doc.log_error) def rule_condition_satisfied(self, doc): if self.for_doc_event == "New": diff --git a/frappe/utils/print_format.py b/frappe/utils/print_format.py index 35842217d1..2488fae0c9 100644 --- a/frappe/utils/print_format.py +++ b/frappe/utils/print_format.py @@ -92,7 +92,11 @@ def download_multi_pdf(doctype, name, format=None, no_letterhead=False, options= pdf_options=options, ) except Exception: - frappe.log_error("Permission Error on doc {} of doctype {}".format(doc_name, doctype_name)) + frappe.log_error( + title = 'Error in Multi PDF download', + message = "Permission Error on doc {} of doctype {}".format(doc_name, doctype_name), + reference_doctype = doctype_name, + reference_name = doc_name) frappe.local.response.filename = "{}.pdf".format(name) frappe.local.response.filecontent = read_multi_pdf(output) diff --git a/frappe/website/doctype/web_form/templates/web_form.html b/frappe/website/doctype/web_form/templates/web_form.html index 2a02aa5f2b..6e0374abdb 100644 --- a/frappe/website/doctype/web_form/templates/web_form.html +++ b/frappe/website/doctype/web_form/templates/web_form.html @@ -70,6 +70,8 @@ data-web-form="{{ name }}" data-web-form-doctype="{{ doc_type }}" data-login-req
{% include 'templates/includes/comments/comments.html' %}
+{%- else -%} +
{%- endif %} {# comments #} {% endblock page_content %} From d55986d56dbd78497dd42c48e3e02dedb0be16e5 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 18 Apr 2022 15:47:05 +0530 Subject: [PATCH 21/45] fix(conflicts): fix conflicts' --- frappe/desk/doctype/system_console/system_console.json | 10 +--------- frappe/desk/query_report.py | 2 +- frappe/email/doctype/newsletter/newsletter.py | 10 +--------- .../doctype/energy_point_rule/energy_point_rule.py | 6 +----- 4 files changed, 4 insertions(+), 24 deletions(-) diff --git a/frappe/desk/doctype/system_console/system_console.json b/frappe/desk/doctype/system_console/system_console.json index bbb815e30e..a851831909 100644 --- a/frappe/desk/doctype/system_console/system_console.json +++ b/frappe/desk/doctype/system_console/system_console.json @@ -1,11 +1,7 @@ { "actions": [ { -<<<<<<< HEAD - "action": "app/console-log", -======= "action": "/app/console-log", ->>>>>>> 6d82805831 (feat(minor): Add document reference to Error Log and doc.log_error) "action_type": "Route", "label": "Logs" }, @@ -90,11 +86,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], -<<<<<<< HEAD - "modified": "2022-04-09 16:35:32.345542", -======= "modified": "2022-04-15 14:15:58.398590", ->>>>>>> 6d82805831 (feat(minor): Add document reference to Error Log and doc.log_error) "modified_by": "Administrator", "module": "Desk", "name": "System Console", @@ -114,4 +106,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py index d1f63a6199..894e82d117 100644 --- a/frappe/desk/query_report.py +++ b/frappe/desk/query_report.py @@ -314,7 +314,7 @@ def get_prepared_report_result(report, filters, dn="", user=None): latest_report_data = {"columns": columns, "result": data} except Exception: - doc.log_error(frappe.get_traceback()) + doc.log_error("Prepared report failed") frappe.delete_doc("Prepared Report", doc.name) frappe.db.commit() doc = None diff --git a/frappe/email/doctype/newsletter/newsletter.py b/frappe/email/doctype/newsletter/newsletter.py index e3726ed5a6..6aa881ed5c 100644 --- a/frappe/email/doctype/newsletter/newsletter.py +++ b/frappe/email/doctype/newsletter/newsletter.py @@ -338,16 +338,8 @@ def send_scheduled_email(): frappe.db.rollback() # wasn't able to send emails :( -<<<<<<< HEAD - frappe.db.set_value("Newsletter", newsletter, "email_sent", 0) - message = ( - f"Newsletter {newsletter} failed to send" "\n\n" f"Traceback: {frappe.get_traceback()}" - ) - frappe.log_error(title="Send Newsletter", message=message) -======= frappe.db.set_value("Newsletter", newsletter_name, "email_sent", 0) - newsletter.log_error('Failed to send newsletter') ->>>>>>> 6d82805831 (feat(minor): Add document reference to Error Log and doc.log_error) + newsletter.log_error("Failed to send newsletter") if not frappe.flags.in_test: frappe.db.commit() diff --git a/frappe/social/doctype/energy_point_rule/energy_point_rule.py b/frappe/social/doctype/energy_point_rule/energy_point_rule.py index 3767a9564e..9d393dde48 100644 --- a/frappe/social/doctype/energy_point_rule/energy_point_rule.py +++ b/frappe/social/doctype/energy_point_rule/energy_point_rule.py @@ -57,11 +57,7 @@ class EnergyPointRule(Document): self.apply_only_once, ) except Exception as e: -<<<<<<< HEAD - frappe.log_error(frappe.get_traceback(), "apply_energy_point") -======= - self.log_error('Energy points failed') ->>>>>>> 6d82805831 (feat(minor): Add document reference to Error Log and doc.log_error) + self.log_error("Energy points failed") def rule_condition_satisfied(self, doc): if self.for_doc_event == "New": From fca6c3d305fc64458c070eedf1ea49e44dc50e96 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 18 Apr 2022 15:59:38 +0530 Subject: [PATCH 22/45] fix(minor): circular imports? --- frappe/cache_manager.py | 1 - frappe/model/naming.py | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/frappe/cache_manager.py b/frappe/cache_manager.py index b15f8f2234..01ccc03753 100644 --- a/frappe/cache_manager.py +++ b/frappe/cache_manager.py @@ -5,7 +5,6 @@ import json import frappe from frappe.desk.notifications import clear_notifications, delete_notification_count_for -from frappe.model.document import Document common_default_keys = ["__default", "__global"] diff --git a/frappe/model/naming.py b/frappe/model/naming.py index 9d1079d995..aa502f5a4c 100644 --- a/frappe/model/naming.py +++ b/frappe/model/naming.py @@ -6,7 +6,6 @@ from typing import TYPE_CHECKING, Optional, Union import frappe from frappe import _ -from frappe.database.sequence import get_next_val, set_next_val from frappe.model import log_types from frappe.query_builder import DocType from frappe.utils import cint, cstr, now_datetime @@ -36,6 +35,8 @@ def set_new_name(doc): doc.name = None if is_autoincremented(doc.doctype, meta): + from frappe.database.sequence import get_next_val + doc.name = get_next_val(doc.doctype) return @@ -322,11 +323,14 @@ def get_default_naming_series(doctype): def validate_name(doctype: str, name: Union[int, str], case: Optional[str] = None): + if not name: frappe.throw(_("No Name Specified for {0}").format(doctype)) if isinstance(name, int): if is_autoincremented(doctype): + from frappe.database.sequence import set_next_val + # this will set the sequence val to be the provided name and set it to be used # so that the sequence will start from the next val of the setted val(name) set_next_val(doctype, name, is_val_used=True) From 6e6fe9521e403aadb73df5e0b20eb683b3f30617 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 18 Apr 2022 16:14:24 +0530 Subject: [PATCH 23/45] fix(linting): no single quotes :'| --- frappe/__init__.py | 23 +++++++++++-------- .../doctype/auto_repeat/auto_repeat.py | 2 +- .../doctype/communication/communication.py | 2 +- frappe/core/doctype/communication/email.py | 2 +- .../core/doctype/data_import/data_import.py | 2 +- .../prepared_report/prepared_report.py | 2 +- frappe/core/doctype/user/user.py | 2 +- frappe/desk/desktop.py | 6 +++-- .../auto_email_report/auto_email_report.py | 5 +++- .../email/doctype/email_queue/email_queue.py | 10 ++++---- .../doctype/notification/notification.py | 8 +++---- .../doctype/event_consumer/event_consumer.py | 2 +- frappe/model/document.py | 8 +++---- frappe/model/workflow.py | 6 ++--- frappe/utils/print_format.py | 9 ++++---- 15 files changed, 49 insertions(+), 40 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 07a580944f..dcc401c036 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -2069,7 +2069,7 @@ def logger( def log_error(title=None, message=None, reference_doctype=None, reference_name=None): - '''Log error to Error Log''' + """Log error to Error Log""" # Parameter ALERT: # the title and message may be swapped @@ -2077,7 +2077,7 @@ def log_error(title=None, message=None, reference_doctype=None, reference_name=N # this hack tries to be smart about whats a title (single line ;-)) and fixes it if message: - if '\n' in title: # traceback sent as title + if "\n" in title: # traceback sent as title traceback, title = title, message else: traceback = message @@ -2086,15 +2086,18 @@ def log_error(title=None, message=None, reference_doctype=None, reference_name=N traceback = get_traceback() if not title: - title = 'Error' + title = "Error" + + return get_doc( + dict( + doctype="Error Log", + error=as_unicode(traceback), + method=title, + reference_doctype=reference_doctype, + reference_name=reference_name, + ) + ).insert(ignore_permissions=True) - return get_doc(dict( - doctype='Error Log', - error=as_unicode(traceback), - method=title, - reference_doctype=reference_doctype, - reference_name=reference_name - )).insert(ignore_permissions=True) def get_desk_link(doctype, name): html = ( diff --git a/frappe/automation/doctype/auto_repeat/auto_repeat.py b/frappe/automation/doctype/auto_repeat/auto_repeat.py index 4b2f83f4b6..d3399f7726 100644 --- a/frappe/automation/doctype/auto_repeat/auto_repeat.py +++ b/frappe/automation/doctype/auto_repeat/auto_repeat.py @@ -189,7 +189,7 @@ class AutoRepeat(Document): if self.notify_by_email and self.recipients: self.send_notification(new_doc) except Exception: - error_log = self.log_error('Auto repeat failed') + error_log = self.log_error("Auto repeat failed") self.disable_auto_repeat() diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index eeddb7c7a1..f0e80c2207 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -450,7 +450,7 @@ def get_contacts(email_strings: List[str], auto_create_contact=False) -> List[st contact.insert(ignore_permissions=True) contact_name = contact.name except Exception: - contact.log_error('Unable to add contact') + contact.log_error("Unable to add contact") if contact_name: contacts.append(contact_name) diff --git a/frappe/core/doctype/communication/email.py b/frappe/core/doctype/communication/email.py index d47b15d360..464bc35a1c 100755 --- a/frappe/core/doctype/communication/email.py +++ b/frappe/core/doctype/communication/email.py @@ -248,7 +248,7 @@ def mark_email_as_seen(name: str = None): frappe.db.commit() # nosemgrep: this will be called in a GET request except Exception: - frappe.log_error('Unable to mark as seen', None, 'Communication', name) + frappe.log_error("Unable to mark as seen", None, "Communication", name) finally: frappe.response.update( diff --git a/frappe/core/doctype/data_import/data_import.py b/frappe/core/doctype/data_import/data_import.py index 3693ea53a4..06282e5831 100644 --- a/frappe/core/doctype/data_import/data_import.py +++ b/frappe/core/doctype/data_import/data_import.py @@ -113,7 +113,7 @@ def start_import(data_import): except Exception: frappe.db.rollback() data_import.db_set("status", "Error") - data_import.log_error('Data import failed') + data_import.log_error("Data import failed") finally: frappe.flags.in_import = False diff --git a/frappe/core/doctype/prepared_report/prepared_report.py b/frappe/core/doctype/prepared_report/prepared_report.py index dbdba19fbe..e35ec43565 100644 --- a/frappe/core/doctype/prepared_report/prepared_report.py +++ b/frappe/core/doctype/prepared_report/prepared_report.py @@ -47,7 +47,7 @@ def run_background(prepared_report): instance.save(ignore_permissions=True) except Exception: - report.log_error('Prepared report failed') + report.log_error("Prepared report failed") instance = frappe.get_doc("Prepared Report", prepared_report) instance.status = "Error" instance.error_message = frappe.get_traceback() diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index ec7374d72a..e81f5ecd99 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -265,7 +265,7 @@ class User(Document): except frappe.OutgoingEmailError: # email server not set, don't send email - self.log_error('Unable to send new password notification') + self.log_error("Unable to send new password notification") @Document.hook def validate_reset_password(self): diff --git a/frappe/desk/desktop.py b/frappe/desk/desktop.py index ae34cf3df2..4c82fe8c73 100644 --- a/frappe/desk/desktop.py +++ b/frappe/desk/desktop.py @@ -338,7 +338,7 @@ def get_desktop_page(page): "onboardings": workspace.onboardings, } except DoesNotExistError: - frappe.log_error('Workspace Missing') + frappe.log_error("Workspace Missing") return {} @@ -469,7 +469,9 @@ def save_new_widget(doc, page, blocks, new_widgets): page: {0} config: {1} exception: {2} - """.format(page, json_config, e) + """.format( + page, json_config, e + ) doc.log_error("Could not save customization", log) return False diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.py b/frappe/email/doctype/auto_email_report/auto_email_report.py index 108cc46792..7a9af6149a 100644 --- a/frappe/email/doctype/auto_email_report/auto_email_report.py +++ b/frappe/email/doctype/auto_email_report/auto_email_report.py @@ -255,7 +255,10 @@ def send_daily(): try: auto_email_report.send() except Exception as e: - auto_email_report.log_error('Failed to send {0} Auto Email Report'.format(auto_email_report.name)) + auto_email_report.log_error( + "Failed to send {0} Auto Email Report".format(auto_email_report.name) + ) + def send_monthly(): """Check reports to be sent monthly""" diff --git a/frappe/email/doctype/email_queue/email_queue.py b/frappe/email/doctype/email_queue/email_queue.py index b3e52ef1e2..61d730829b 100644 --- a/frappe/email/doctype/email_queue/email_queue.py +++ b/frappe/email/doctype/email_queue/email_queue.py @@ -198,7 +198,7 @@ class SendMailContext: traceback_string = "".join(traceback.format_tb(exc_tb)) traceback_string += f"\n Queue Name: {self.queue_doc.name}" - self.queue_doc.log_error('Email sending failed', traceback_string) + self.queue_doc.log_error("Email sending failed", traceback_string) @property def smtp_session(self): @@ -623,9 +623,11 @@ class QueueBuilder: except frappe.InvalidEmailAddressError: # bad Email Address - don't add to queue self.log_error( - title = 'Invalid email address', - message = 'Invalid email address Sender: {0}, Recipients: {1}, \nTraceback: {2} ' - .format(self.sender, ', '.join(self.final_recipients()), traceback.format_exc())) + title="Invalid email address", + message="Invalid email address Sender: {0}, Recipients: {1}, \nTraceback: {2} ".format( + self.sender, ", ".join(self.final_recipients()), traceback.format_exc() + ), + ) return d = { diff --git a/frappe/email/doctype/notification/notification.py b/frappe/email/doctype/notification/notification.py index e868c0ceb6..5543ae6b5d 100644 --- a/frappe/email/doctype/notification/notification.py +++ b/frappe/email/doctype/notification/notification.py @@ -141,7 +141,7 @@ def get_context(context): self.create_system_notification(doc, context) except: - self.log_error('Failed to send Notification') + self.log_error("Failed to send Notification") if self.set_property_after_alert: allow_update = True @@ -168,7 +168,7 @@ def get_context(context): doc.save(ignore_permissions=True) doc.flags.in_notification_update = False except Exception: - self.log_error('Document update failed') + self.log_error("Document update failed") def create_system_notification(self, doc, context): subject = self.subject @@ -432,8 +432,8 @@ def evaluate_alert(doc, alert, event): if event == "Value Change" and not doc.is_new(): if not frappe.db.has_column(doc.doctype, alert.value_changed): - alert.db_set('enabled', 0) - alert.log_error('Notification {0} has been disabled due to missing field'.format(alert.name)) + alert.db_set("enabled", 0) + alert.log_error("Notification {0} has been disabled due to missing field".format(alert.name)) return doc_before_save = doc.get_doc_before_save() diff --git a/frappe/event_streaming/doctype/event_consumer/event_consumer.py b/frappe/event_streaming/doctype/event_consumer/event_consumer.py index dd3d2ec74c..bcd3d3be39 100644 --- a/frappe/event_streaming/doctype/event_consumer/event_consumer.py +++ b/frappe/event_streaming/doctype/event_consumer/event_consumer.py @@ -213,5 +213,5 @@ def has_consumer_access(consumer, update_log): else: return frappe.safe_eval(condition, frappe._dict(doc=doc)) except Exception as e: - consumer.log_error('has_consumer_access error') + consumer.log_error("has_consumer_access error") return False diff --git a/frappe/model/document.py b/frappe/model/document.py index a7b59ae749..67e1de0932 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -1363,12 +1363,10 @@ class Document(BaseDocument): frappe.local.flags.commit = True def log_error(self, title=None, message=None): - '''Helper function to create an Error Log''' + """Helper function to create an Error Log""" return frappe.log_error( - message = message, - title = title, - reference_doctype = self.doctype, - reference_name = self.name) + message=message, title=title, reference_doctype=self.doctype, reference_name=self.name + ) def get_signature(self): """Returns signature (hash) for private URL.""" diff --git a/frappe/model/workflow.py b/frappe/model/workflow.py index 092ad4f764..96fd710d91 100644 --- a/frappe/model/workflow.py +++ b/frappe/model/workflow.py @@ -254,9 +254,9 @@ def bulk_workflow_approval(docnames, doctype, action): frappe.db.rollback() frappe.log_error( - title = "Workflow {0} threw an error for {1} {2}".format(action, doctype, docname), - reference_doctype = 'Workflow', - reference_name = action + title="Workflow {0} threw an error for {1} {2}".format(action, doctype, docname), + reference_doctype="Workflow", + reference_name=action, ) finally: if not message_dict: diff --git a/frappe/utils/print_format.py b/frappe/utils/print_format.py index 2488fae0c9..87fb646f47 100644 --- a/frappe/utils/print_format.py +++ b/frappe/utils/print_format.py @@ -93,10 +93,11 @@ def download_multi_pdf(doctype, name, format=None, no_letterhead=False, options= ) except Exception: frappe.log_error( - title = 'Error in Multi PDF download', - message = "Permission Error on doc {} of doctype {}".format(doc_name, doctype_name), - reference_doctype = doctype_name, - reference_name = doc_name) + title="Error in Multi PDF download", + message="Permission Error on doc {} of doctype {}".format(doc_name, doctype_name), + reference_doctype=doctype_name, + reference_name=doc_name, + ) frappe.local.response.filename = "{}.pdf".format(name) frappe.local.response.filecontent = read_multi_pdf(output) From d9fbee3b9c77587960fcce2b1f7817ff2267c465 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 18 Apr 2022 16:19:46 +0530 Subject: [PATCH 24/45] fix(minor): frappe.log_error --- frappe/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/__init__.py b/frappe/__init__.py index dcc401c036..e3a3769480 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -2076,6 +2076,7 @@ def log_error(title=None, message=None, reference_doctype=None, reference_name=N # the better API for this is log_error(title, message), and used in many cases this way # this hack tries to be smart about whats a title (single line ;-)) and fixes it + traceback = None if message: if "\n" in title: # traceback sent as title traceback, title = title, message From 626ef14e1ef03d2f02e17a83c4c04bb1ad1da927 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 18 Apr 2022 16:23:11 +0530 Subject: [PATCH 25/45] fix(linting): fix quotes --- frappe/email/doctype/email_account/email_account.py | 5 +++-- frappe/email/queue.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 65586de31d..73ab13b851 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -667,7 +667,7 @@ class EmailAccount(Document): try: email_server = self.get_incoming_server(in_receive=True) except Exception: - self.log_error(title=_("Error while connecting to email account {0}").format(self.name)) + self.log_error("Email Connection Error") if not email_server: return @@ -679,7 +679,8 @@ class EmailAccount(Document): message = safe_encode(message) email_server.imap.append("Sent", "\\Seen", imaplib.Time2Internaldate(time.time()), message) except Exception: - self.log_error(title="EmailAccount.append_email_to_sent_folder") + self.log_error("Unable to add to Sent folder") + @frappe.whitelist() def get_append_to( diff --git a/frappe/email/queue.py b/frappe/email/queue.py index ac873182a7..b92dea3e65 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -170,7 +170,7 @@ def flush(from_test=False): is_background_task = not from_test func(email_queue_name=row.name, is_background_task=is_background_task) except Exception: - frappe.get_doc('Email Queue', row.name).log_error() + frappe.get_doc("Email Queue", row.name).log_error() def get_queue(): From 930ae45fb8669aae8c2f9fb2bd89e08f4fb170c0 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Mon, 18 Apr 2022 17:27:33 +0530 Subject: [PATCH 26/45] fix(test): add a test for Error Log --- frappe/core/doctype/error_log/error_log.json | 5 +++-- frappe/core/doctype/error_log/test_error_log.py | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/frappe/core/doctype/error_log/error_log.json b/frappe/core/doctype/error_log/error_log.json index e643bbb090..b2ab516bba 100644 --- a/frappe/core/doctype/error_log/error_log.json +++ b/frappe/core/doctype/error_log/error_log.json @@ -50,7 +50,7 @@ "icon": "fa fa-warning-sign", "idx": 1, "links": [], - "modified": "2022-04-18 14:51:30.604304", + "modified": "2022-04-18 17:25:47.406873", "modified_by": "Administrator", "module": "Core", "name": "Error Log", @@ -71,5 +71,6 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "ASC", - "states": [] + "states": [], + "title_field": "method" } \ No newline at end of file diff --git a/frappe/core/doctype/error_log/test_error_log.py b/frappe/core/doctype/error_log/test_error_log.py index e20ac92650..a8511b238e 100644 --- a/frappe/core/doctype/error_log/test_error_log.py +++ b/frappe/core/doctype/error_log/test_error_log.py @@ -9,4 +9,8 @@ import frappe class TestErrorLog(unittest.TestCase): - pass + def test_error_log(self): + """let's do an error log on error log?""" + doc = frappe.new_doc("Error Log") + error = doc.log_error("This is an error") + self.assertEqual(error.doctype, "Error Log") From de8f066dce7878bcff56b3aa7bde8b4aaea34189 Mon Sep 17 00:00:00 2001 From: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Date: Mon, 18 Apr 2022 17:31:36 +0530 Subject: [PATCH 27/45] fix: Set dependant property of grid fields while rendering (#16548) --- frappe/public/js/frappe/form/grid_row.js | 52 ++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index c12ac23319..4bba8ae7ad 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -595,6 +595,8 @@ export default class GridRow { // to get update df for the row let df = this.docfields.find(field => field.fieldname === col[0].fieldname); + this.set_dependant_property(df); + let colsize = col[1]; let txt = this.doc ? frappe.format(this.doc[df.fieldname], df, null, this.doc) : @@ -633,6 +635,56 @@ export default class GridRow { } } + set_dependant_property(df) { + if (!df.reqd && df.mandatory_depends_on && + this.evaluate_depends_on_value(df.mandatory_depends_on)) { + df.reqd = 1; + } + + if (!df.read_only && df.read_only_depends_on && + this.evaluate_depends_on_value(df.read_only_depends_on)) { + df.read_only = 1; + } + } + + evaluate_depends_on_value(expression) { + let out = null; + let doc = this.doc; + + if (!doc) return; + + let parent = this.frm ? this.frm.doc : this.doc || null; + + if (typeof (expression) === 'boolean') { + out = expression; + + } else if (typeof (expression) === 'function') { + out = expression(doc); + + } else if (expression.substr(0, 5)=='eval:') { + try { + out = frappe.utils.eval(expression.substr(5), { doc, parent }); + if (parent && parent.istable && expression.includes('is_submittable')) { + out = true; + } + } catch (e) { + frappe.throw(__('Invalid "depends_on" expression')); + } + + } else if (expression.substr(0, 3)=='fn:' && this.frm) { + out = this.frm.script_manager.trigger(expression.substr(3), this.doctype, this.docname); + } else { + var value = doc[expression]; + if ($.isArray(value)) { + out = !!value.length; + } else { + out = !!value; + } + } + + return out; + } + show_search_row() { // show or remove search columns based on grid rows this.show_search = this.frm && this.frm.doc && From 95a13853c7407c48afc6150033e9e0b977602b8e Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 18 Apr 2022 17:34:44 +0530 Subject: [PATCH 28/45] ci(Mergify): configuration update (#16655) Signed-off-by: Ankush Menat [skip ci] --- .mergify.yml | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/.mergify.yml b/.mergify.yml index 838ce75835..7f4c084e30 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -53,3 +53,43 @@ pull_request_rules: {{ title }} (#{{ number }}) {{ body }} + + - name: backport to develop + conditions: + - label="backport develop" + actions: + backport: + branches: + - develop + assignees: + - "{{ author }}" + + - name: backport to version-13-hotfix + conditions: + - label="backport version-13-hotfix" + actions: + backport: + branches: + - version-13-hotfix + assignees: + - "{{ author }}" + + - name: backport to version-13-pre-release + conditions: + - label="backport version-13-pre-release" + actions: + backport: + branches: + - version-13-pre-release + assignees: + - "{{ author }}" + + - name: backport to version-12-hotfix + conditions: + - label="backport version-12-hotfix" + actions: + backport: + branches: + - version-12-hotfix + assignees: + - "{{ author }}" \ No newline at end of file From 7b19c91c373c3820f7a3c029b1df25ca42b0a913 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 18 Apr 2022 19:44:01 +0530 Subject: [PATCH 29/45] fix: kanban board without title field (#16659) --- frappe/public/js/frappe/list/base_list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/list/base_list.js b/frappe/public/js/frappe/list/base_list.js index d5ee82acce..f64ae39d1b 100644 --- a/frappe/public/js/frappe/list/base_list.js +++ b/frappe/public/js/frappe/list/base_list.js @@ -124,7 +124,7 @@ frappe.views.BaseList = class BaseList { // df is passed const df = fieldname; fieldname = df.fieldname; - doctype = df.parent; + doctype = df.parent || doctype; } if (!this.fields) this.fields = []; From afebaa0a23ef066a83339fe4527929c3d1225a3e Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 14 Apr 2022 13:43:43 +0530 Subject: [PATCH 30/45] test: less flaky date control test --- cypress/integration/control_date.js | 58 +++++++++++++++++------------ cypress/support/commands.js | 14 +++++++ 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/cypress/integration/control_date.js b/cypress/integration/control_date.js index 35c585306c..5093c675cf 100644 --- a/cypress/integration/control_date.js +++ b/cypress/integration/control_date.js @@ -1,23 +1,27 @@ context('Date Control', () => { before(() => { cy.login(); - cy.visit('/app/doctype'); - return cy.window().its('frappe').then(frappe => { - return frappe.xcall('frappe.tests.ui_test_helpers.create_doctype', { - name: 'Test Date Control', - fields: [ - { - "label": "Date", - "fieldname": "date", - "fieldtype": "Date", - "in_list_view": 1 - }, - ] - }); - }); + cy.visit('/app'); }); + + function get_dialog(date_field_options) { + return cy.dialog({ + title: 'Date', + fields: [{ + "label": "Date", + "fieldname": "date", + "fieldtype": "Date", + "in_list_view": 1, + ...date_field_options + }] + }); + } + it('Selecting a date from the datepicker', () => { - cy.new_form('Test Date Control'); + cy.clear_dialogs(); + cy.clear_datepickers(); + + get_dialog().as('dialog'); cy.get_field('date', 'Date').click(); cy.get('.datepicker--nav-title').click(); cy.get('.datepicker--nav-title').click({force: true}); @@ -28,12 +32,16 @@ context('Date Control', () => { cy.get('.datepicker--months > .datepicker--cells > .datepicker--cell[data-month=0]').click(); cy.get('.datepicker--days > .datepicker--cells > .datepicker--cell[data-date=15]').click(); - //Verifying if the selected date is displayed in the date field - cy.get_field('date', 'Date').should('have.value', '01-15-2020'); + // Verify if the selected date is set the date field + cy.window().its('cur_dialog.fields_dict.date.value').should('be.equal', '2020-01-15'); }); it('Checking next and previous button', () => { - cy.get_field('date', 'Date').click(); + cy.clear_dialogs(); + cy.clear_datepickers(); + + get_dialog({ default: '2020-01-15' }).as('dialog'); + cy.get_field('date', 'Date').click(); //Clicking on the next button in the datepicker cy.get('.datepicker--nav-action[data-action=next]').click(); @@ -42,7 +50,7 @@ context('Date Control', () => { cy.get('.datepicker--cell[data-date=15]').click({force: true}); //Verifying if the selected date has been displayed in the date field - cy.get_field('date', 'Date').should('have.value', '02-15-2020'); + cy.window().its('cur_dialog.fields_dict.date.value').should('be.equal', '2020-02-15'); cy.wait(500); cy.get_field('date', 'Date').click(); @@ -53,19 +61,23 @@ context('Date Control', () => { cy.get('.datepicker--cell[data-date=15]').click({force: true}); //Verifying if the selected date has been displayed in the date field - cy.get_field('date', 'Date').should('have.value', '01-15-2020'); + cy.window().its('cur_dialog.fields_dict.date.value').should('be.equal', '2020-01-15'); }); it('Clicking on "Today" button gives todays date', () => { - cy.get_field('date', 'Date').click(); + cy.clear_dialogs(); + cy.clear_datepickers(); + + get_dialog({ default: '2020-01-15' }).as('dialog'); + cy.get_field('date', 'Date').click(); //Clicking on "Today" button cy.get('.datepicker--button').click(); //Picking up the todays date - const todays_date = Cypress.moment().format('MM-DD-YYYY'); + const todays_date = new Date().toJSON().split('T')[0]; //Verifying if clicking on "Today" button matches today's date - cy.get_field('date', 'Date').should('have.value', todays_date); + cy.window().its('cur_dialog.fields_dict.date.value').should('be.equal', todays_date); }); }); \ No newline at end of file diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 4847dbcf12..14ab063b59 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -258,6 +258,20 @@ Cypress.Commands.add('hide_dialog', () => { cy.get('.modal:visible').should('not.exist'); }); +Cypress.Commands.add('clear_dialogs', () => { + cy.window().then((win) => { + win.$('.modal, .modal-backdrop').remove(); + }); + cy.get('.modal').should('not.exist'); +}) + +Cypress.Commands.add('clear_datepickers', () => { + cy.window().then((win) => { + win.$('.datepicker').remove(); + }); + cy.get('.datepicker').should('not.exist'); +}) + Cypress.Commands.add('insert_doc', (doctype, args, ignore_duplicate) => { return cy .window() From 9482ac43993d11d12bd1205f88041ef3b59508be Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Thu, 14 Apr 2022 13:49:39 +0530 Subject: [PATCH 31/45] style: semicolons --- cypress/support/commands.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 14ab063b59..ec24212f01 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -263,14 +263,14 @@ Cypress.Commands.add('clear_dialogs', () => { win.$('.modal, .modal-backdrop').remove(); }); cy.get('.modal').should('not.exist'); -}) +}); Cypress.Commands.add('clear_datepickers', () => { cy.window().then((win) => { win.$('.datepicker').remove(); }); cy.get('.datepicker').should('not.exist'); -}) +}); Cypress.Commands.add('insert_doc', (doctype, args, ignore_duplicate) => { return cy From bb58ee5ee62f2cc8a29969bd60e395d9e9932aa3 Mon Sep 17 00:00:00 2001 From: phot0n Date: Wed, 13 Apr 2022 22:56:20 +0530 Subject: [PATCH 32/45] feat(minor): add ignore_xss_filter to customize form field --- frappe/custom/doctype/customize_form/customize_form.py | 1 + .../customize_form_field/customize_form_field.json | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py index b4ccb21167..12ef945288 100644 --- a/frappe/custom/doctype/customize_form/customize_form.py +++ b/frappe/custom/doctype/customize_form/customize_form.py @@ -596,6 +596,7 @@ docfield_properties = { "in_preview": "Check", "bold": "Check", "no_copy": "Check", + "ignore_xss_filter": "Check", "hidden": "Check", "collapsible": "Check", "collapsible_depends_on": "Data", diff --git a/frappe/custom/doctype/customize_form_field/customize_form_field.json b/frappe/custom/doctype/customize_form_field/customize_form_field.json index cc446e321e..b991726123 100644 --- a/frappe/custom/doctype/customize_form_field/customize_form_field.json +++ b/frappe/custom/doctype/customize_form_field/customize_form_field.json @@ -21,6 +21,7 @@ "in_global_search", "in_preview", "bold", + "ignore_xss_filter", "no_copy", "allow_in_quick_entry", "translatable", @@ -453,13 +454,19 @@ "hidden": 1, "label": "Is System Generated", "read_only": 1 + }, + { + "default": "0", + "fieldname": "ignore_xss_filter", + "fieldtype": "Check", + "label": "Ignore XSS Filter" } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-03-31 12:05:11.799654", + "modified": "2022-04-13 22:31:14.162661", "modified_by": "Administrator", "module": "Custom", "name": "Customize Form Field", From 1aa267a51906040911ca14beae54d7a2b13bdd9a Mon Sep 17 00:00:00 2001 From: phot0n Date: Tue, 19 Apr 2022 12:41:33 +0530 Subject: [PATCH 33/45] chore: move the field to permission section * chore: fix type in description of ignore_xss_filter in docfield doctype --- frappe/core/doctype/docfield/docfield.json | 4 ++-- .../doctype/customize_form_field/customize_form_field.json | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/frappe/core/doctype/docfield/docfield.json b/frappe/core/doctype/docfield/docfield.json index 9e9aaf489b..5c7d06c93a 100644 --- a/frappe/core/doctype/docfield/docfield.json +++ b/frappe/core/doctype/docfield/docfield.json @@ -347,7 +347,7 @@ }, { "default": "0", - "description": "Don't HTML Encode HTML tags like <script> or just characters like < or >, as they could be intentionally used in this field", + "description": "Don't encode HTML tags like <script> or just characters like < or >, as they could be intentionally used in this field", "fieldname": "ignore_xss_filter", "fieldtype": "Check", "label": "Ignore XSS Filter" @@ -547,7 +547,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-03-02 17:07:32.117897", + "modified": "2022-04-19 12:27:28.641580", "modified_by": "Administrator", "module": "Core", "name": "DocField", diff --git a/frappe/custom/doctype/customize_form_field/customize_form_field.json b/frappe/custom/doctype/customize_form_field/customize_form_field.json index b991726123..a7a8eff950 100644 --- a/frappe/custom/doctype/customize_form_field/customize_form_field.json +++ b/frappe/custom/doctype/customize_form_field/customize_form_field.json @@ -21,7 +21,6 @@ "in_global_search", "in_preview", "bold", - "ignore_xss_filter", "no_copy", "allow_in_quick_entry", "translatable", @@ -47,6 +46,7 @@ "report_hide", "remember_last_selected_value", "hide_border", + "ignore_xss_filter", "property_depends_on_section", "mandatory_depends_on", "column_break_33", @@ -457,6 +457,7 @@ }, { "default": "0", + "description": "Don't encode HTML tags like <script> or just characters like < or >, as they could be intentionally used in this field", "fieldname": "ignore_xss_filter", "fieldtype": "Check", "label": "Ignore XSS Filter" From 317ff3b9e23ecc27626a8a4ab3b2ce307daf7636 Mon Sep 17 00:00:00 2001 From: phot0n Date: Tue, 19 Apr 2022 13:39:51 +0530 Subject: [PATCH 34/45] chore: remove tag link patch --- frappe/patches.txt | 1 - frappe/patches/v12_0/copy_to_parent_for_tags.py | 7 ------- 2 files changed, 8 deletions(-) delete mode 100644 frappe/patches/v12_0/copy_to_parent_for_tags.py diff --git a/frappe/patches.txt b/frappe/patches.txt index bc2bc22637..845ccee09a 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -106,7 +106,6 @@ frappe.patches.v12_0.set_default_incoming_email_port frappe.patches.v12_0.update_global_search frappe.patches.v12_0.setup_tags frappe.patches.v12_0.update_auto_repeat_status_and_not_submittable -frappe.patches.v12_0.copy_to_parent_for_tags frappe.patches.v12_0.create_notification_settings_for_user frappe.patches.v11_0.make_all_prepared_report_attachments_private #2019-11-26 frappe.patches.v12_0.setup_email_linking diff --git a/frappe/patches/v12_0/copy_to_parent_for_tags.py b/frappe/patches/v12_0/copy_to_parent_for_tags.py deleted file mode 100644 index ae3702a0d5..0000000000 --- a/frappe/patches/v12_0/copy_to_parent_for_tags.py +++ /dev/null @@ -1,7 +0,0 @@ -import frappe - - -def execute(): - - frappe.db.sql("UPDATE `tabTag Link` SET parenttype=document_type") - frappe.db.sql("UPDATE `tabTag Link` SET parent=document_name") From ef6bb79f65e0a1867926ef769ad8de16455adf2d Mon Sep 17 00:00:00 2001 From: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Date: Tue, 19 Apr 2022 15:51:03 +0530 Subject: [PATCH 35/45] test: Typing on focused input issue (#16669) --- cypress/integration/control_barcode.js | 2 ++ cypress/integration/kanban.js | 1 + 2 files changed, 3 insertions(+) diff --git a/cypress/integration/control_barcode.js b/cypress/integration/control_barcode.js index 03ab61fac4..85a3182397 100644 --- a/cypress/integration/control_barcode.js +++ b/cypress/integration/control_barcode.js @@ -20,6 +20,7 @@ context('Control Barcode', () => { it('should generate barcode on setting a value', () => { get_dialog_with_barcode().as('dialog'); + cy.focused().blur(); cy.get('.frappe-control[data-fieldname=barcode]').findByRole('textbox') .type('123456789') .blur(); @@ -36,6 +37,7 @@ context('Control Barcode', () => { it('should reset when input is cleared', () => { get_dialog_with_barcode().as('dialog'); + cy.focused().blur(); cy.get('.frappe-control[data-fieldname=barcode]').findByRole('textbox') .type('123456789') .blur(); diff --git a/cypress/integration/kanban.js b/cypress/integration/kanban.js index bb9aaf3d8c..c7f3f08336 100644 --- a/cypress/integration/kanban.js +++ b/cypress/integration/kanban.js @@ -10,6 +10,7 @@ context('Kanban Board', () => { cy.get('.page-actions .custom-btn-group button').click(); cy.get('.page-actions .custom-btn-group ul.dropdown-menu li').contains('Kanban').click(); + cy.focused().blur(); cy.fill_field('board_name', 'ToDo Kanban', 'Data'); cy.fill_field('field_name', 'Status', 'Select'); cy.click_modal_primary_button('Save'); From d1978d8e62e01e23aba3aec8eb21bd0b130399d2 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 19 Apr 2022 16:49:08 +0530 Subject: [PATCH 36/45] fix: check dates in frappe realm new Date in Cypress context and new Date in frappe context differs sometimes in CI --- cypress/integration/control_date.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cypress/integration/control_date.js b/cypress/integration/control_date.js index 5093c675cf..6d9f0b9bcc 100644 --- a/cypress/integration/control_date.js +++ b/cypress/integration/control_date.js @@ -68,16 +68,15 @@ context('Date Control', () => { cy.clear_dialogs(); cy.clear_datepickers(); - get_dialog({ default: '2020-01-15' }).as('dialog'); + get_dialog().as('dialog'); cy.get_field('date', 'Date').click(); //Clicking on "Today" button cy.get('.datepicker--button').click(); - //Picking up the todays date - const todays_date = new Date().toJSON().split('T')[0]; - //Verifying if clicking on "Today" button matches today's date - cy.window().its('cur_dialog.fields_dict.date.value').should('be.equal', todays_date); + cy.window().then(win => { + expect(win.cur_dialog.fields_dict.date.value).to.be.equal(win.frappe.datetime.get_today()); + }); }); }); \ No newline at end of file From fc4e2780bd1e82f22d91920d50abbd7f11b8b379 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Tue, 19 Apr 2022 16:49:34 +0530 Subject: [PATCH 37/45] chore: pretty logging for dialog command --- cypress/support/commands.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/cypress/support/commands.js b/cypress/support/commands.js index ec24212f01..026c622e78 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -241,8 +241,20 @@ Cypress.Commands.add('clear_cache', () => { }); Cypress.Commands.add('dialog', opts => { - return cy.window().then(win => { - var d = new win.frappe.ui.Dialog(opts); + return cy.window({ log: false }).its('frappe', { log: false }).then(frappe => { + Cypress.log({ + name: "dialog", + displayName: "dialog", + message: 'frappe.ui.Dialog', + consoleProps: () => { + return { + options: opts, + dialog: d + } + } + }); + + var d = new frappe.ui.Dialog(opts); d.show(); return d; }); From b59619f764a09110e8a8a07dff5d4bc6352a4746 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 19 Apr 2022 17:29:09 +0530 Subject: [PATCH 38/45] chore: Update creds to allow updates on protected branch (#16672) --- .github/workflows/release.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 52fc595c96..a6c1243f64 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,6 +12,7 @@ jobs: uses: actions/checkout@v2 with: fetch-depth: 0 + persist-credentials: false - name: Setup Node.js v14 uses: actions/setup-node@v2 with: @@ -22,4 +23,10 @@ jobs: - name: Create Release env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.RELEASE_TOKEN }} + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + GIT_AUTHOR_NAME: "Frappe PR Bot" + GIT_AUTHOR_EMAIL: "developers@frappe.io" + GIT_COMMITTER_NAME: "Frappe PR Bot" + GIT_COMMITTER_EMAIL: "developers@frappe.io" run: npx semantic-release \ No newline at end of file From aef0c0f66e4b1997ef946998e6822de7a8ee58cf Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 19 Apr 2022 19:21:25 +0530 Subject: [PATCH 39/45] fix: Handle unable to read commit errors --- frappe/commands/utils.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index bb1a8bc2d2..499dd61421 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -1024,6 +1024,7 @@ def set_config(context, key, value, global_=False, parse=False, as_dict=False): def get_version(output): """Show the versions of all the installed apps.""" from git import Repo + from git.exc import InvalidGitRepositoryError from frappe.utils.change_log import get_app_branch from frappe.utils.commands import render_table @@ -1034,12 +1035,16 @@ def get_version(output): for app in sorted(frappe.get_all_apps()): module = frappe.get_module(app) app_hooks = frappe.get_module(app + ".hooks") - repo = Repo(frappe.get_app_path(app, "..")) app_info = frappe._dict() + + try: + app_info.commit = Repo(frappe.get_app_path(app, "..")).head.object.hexsha[:7] + except InvalidGitRepositoryError: + app_info.commit = "" + app_info.app = app app_info.branch = get_app_branch(app) - app_info.commit = repo.head.object.hexsha[:7] app_info.version = getattr(app_hooks, f"{app_info.branch}_version", None) or module.__version__ data.append(app_info) From 8b010e1732b1095e9208d36a5c2dcab16855e7e6 Mon Sep 17 00:00:00 2001 From: Pruthvi Patel Date: Tue, 19 Apr 2022 20:17:22 +0530 Subject: [PATCH 40/45] refactor: make `frappe.db.bulk_insert` work as expected (#16527) ## Issue `frappe.db.bulk_insert` is not working as expected: - It will not insert any row if there are less than 3 values - It will not add 1st row at all. e.g if I'm adding 5 values, it will only add 4. - It will add values one by one after 2 values, instead it should have inserted items (in db) in chunk of 10000 (as per the code written before). ## Changes Made - Solved above issues - use better way to chunk list - Added Postgres support for bulk_insert API And now `bulk_insert` will only do **1 db call for each 10000** values. Note: For testing purpose I made `Test Bulk Insert` doctype and keep chunk size of 100. ## Before ![image](https://user-images.githubusercontent.com/43115036/161979365-c1100745-7602-47d2-a9b8-62d797d2039f.png) ## After ![image](https://user-images.githubusercontent.com/43115036/161978344-3c17d56b-2195-40f4-b00c-e9478d4083f1.png) nodocs --- frappe/database/database.py | 29 +++++++++++++---------------- frappe/tests/test_db.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index 424bcbbc63..411888df34 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -1228,7 +1228,7 @@ class Database(object): frappe.flags.touched_tables = set() frappe.flags.touched_tables.update(tables) - def bulk_insert(self, doctype, fields, values, ignore_duplicates=False): + def bulk_insert(self, doctype, fields, values, ignore_duplicates=False, *, chunk_size=10_000): """ Insert multiple records at a time @@ -1236,22 +1236,19 @@ class Database(object): :param fields: list of fields :params values: list of list of values """ - insert_list = [] - fields = ", ".join("`" + field + "`" for field in fields) - for idx, value in enumerate(values): - insert_list.append(tuple(value)) - if idx and (idx % 10000 == 0 or idx < len(values) - 1): - self.sql( - """INSERT {ignore_duplicates} INTO `tab{doctype}` ({fields}) VALUES {values}""".format( - ignore_duplicates="IGNORE" if ignore_duplicates else "", - doctype=doctype, - fields=fields, - values=", ".join(["%s"] * len(insert_list)), - ), - tuple(insert_list), - ) - insert_list = [] + table = frappe.qb.DocType(doctype) + for start_index in range(0, len(values), chunk_size): + query = frappe.qb.into(table) + if ignore_duplicates: + # Pypika does not have same api for ignoring duplicates + if frappe.conf.db_type == "mariadb": + query = query.ignore() + elif frappe.conf.db_type == "postgres": + query = query.on_conflict().do_nothing() + + values_to_insert = values[start_index : start_index + chunk_size] + query.columns(fields).insert(*values_to_insert).run() def enqueue_jobs_after_commit(): diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index f722ad1d65..5b469cd5db 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -4,6 +4,7 @@ import datetime import inspect import unittest +from math import ceil from random import choice from unittest.mock import patch @@ -445,6 +446,33 @@ class TestDB(unittest.TestCase): self.assertEqual(frappe.db.exists(dt, [["name", "=", dn]]), dn) + def test_bulk_insert(self): + current_count = frappe.db.count("ToDo") + test_body = f"test_bulk_insert - {random_string(10)}" + chunk_size = 10 + + for number_of_values in (1, 2, 5, 27): + current_transaction_writes = frappe.db.transaction_writes + + frappe.db.bulk_insert( + "ToDo", + ["name", "description"], + [[f"ToDo Test Bulk Insert {i}", test_body] for i in range(number_of_values)], + ignore_duplicates=True, + chunk_size=chunk_size, + ) + + # check that all records were inserted + self.assertEqual(number_of_values, frappe.db.count("ToDo") - current_count) + + # check if inserts were done in chunks + expected_number_of_writes = ceil(number_of_values / chunk_size) + self.assertEqual( + expected_number_of_writes, frappe.db.transaction_writes - current_transaction_writes + ) + + frappe.db.delete("ToDo", {"description": test_body}) + @run_only_if(db_type_is.MARIADB) class TestDDLCommandsMaria(unittest.TestCase): From 91e0cac029ad3a969ed04dd0d61b699ba84d69a8 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 20 Apr 2022 11:25:38 +0530 Subject: [PATCH 41/45] fix: Check for required services running after frappe init Each site on a single app server can run on separate DBMS' on separate servers. This site specific config resides in each site's site_config.json file. Thereby, we need to load site's config before checking for service's availability. --- frappe/migrate.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/migrate.py b/frappe/migrate.py index bb83fa5b6d..1c249dfdb1 100644 --- a/frappe/migrate.py +++ b/frappe/migrate.py @@ -159,13 +159,13 @@ class SiteMigration: """Run Migrate operation on site specified. This method initializes and destroys connections to the site database. """ - if not self.required_services_running(): - raise SystemExit(1) - if site: frappe.init(site=site) frappe.connect() + if not self.required_services_running(): + raise SystemExit(1) + self.setUp() try: self.pre_schema_updates() From 11360c5fd810c83bd61731e8f430d84ec95dfdcf Mon Sep 17 00:00:00 2001 From: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Date: Wed, 20 Apr 2022 13:02:42 +0530 Subject: [PATCH 42/45] fix: Check mandatory_depends_on also while checking mandatory on save (#16515) --- frappe/public/js/frappe/form/save.js | 39 +++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/form/save.js b/frappe/public/js/frappe/form/save.js index 90516b7c0a..f1cb42250a 100644 --- a/frappe/public/js/frappe/form/save.js +++ b/frappe/public/js/frappe/form/save.js @@ -130,7 +130,8 @@ frappe.ui.form.save = function (frm, action, callback, btn) { folded = frm.layout.folded; } - if (df.reqd && !frappe.model.has_value(doc.doctype, doc.name, df.fieldname)) { + if (is_docfield_mandatory(doc, df) && + !frappe.model.has_value(doc.doctype, doc.name, df.fieldname)) { has_errors = true; error_fields[error_fields.length] = __(df.label); // scroll to field @@ -173,6 +174,42 @@ frappe.ui.form.save = function (frm, action, callback, btn) { return !has_errors; }; + let is_docfield_mandatory = function(doc, df) { + if (df.reqd) return true; + if (!df.mandatory_depends_on || !doc) return; + + let out = null; + let expression = df.mandatory_depends_on; + let parent = frappe.get_meta(df.parent); + + if (typeof (expression) === 'boolean') { + out = expression; + + } else if (typeof (expression) === 'function') { + out = expression(doc); + + } else if (expression.substr(0, 5) == 'eval:') { + try { + out = frappe.utils.eval(expression.substr(5), { doc, parent }); + if (parent && parent.istable && expression.includes('is_submittable')) { + out = true; + } + } catch (e) { + frappe.throw(__('Invalid "mandatory_depends_on" expression')); + } + + } else { + var value = doc[expression]; + if ($.isArray(value)) { + out = !!value.length; + } else { + out = !!value; + } + } + + return out; + }; + const scroll_to = (fieldname) => { frm.scroll_to_field(fieldname); frm.scroll_set = true; From dcee40046a0dc65a78b05f54173eb2bb4f014890 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 20 Apr 2022 15:16:04 +0530 Subject: [PATCH 43/45] perf!: simpler frappe.get_system_setting (#16685) Not sure why this needs to be "cached" in locals again when db object already caches it in value_cache. --- frappe/__init__.py | 5 +---- frappe/core/doctype/system_settings/system_settings.py | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 10c8afbf23..cbf62a8d7d 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -217,7 +217,6 @@ def init(site, sites_path=None, new_site=False): local.module_app = None local.app_modules = None - local.system_settings = _dict() local.user = None local.user_perms = None @@ -2139,9 +2138,7 @@ def safe_eval(code, eval_globals=None, eval_locals=None): def get_system_settings(key): - if key not in local.system_settings: - local.system_settings.update({key: db.get_single_value("System Settings", key)}) - return local.system_settings.get(key) + return db.get_single_value("System Settings", key, cache=True) def get_active_domains(): diff --git a/frappe/core/doctype/system_settings/system_settings.py b/frappe/core/doctype/system_settings/system_settings.py index 3d01015087..e4d36b7fc7 100644 --- a/frappe/core/doctype/system_settings/system_settings.py +++ b/frappe/core/doctype/system_settings/system_settings.py @@ -53,7 +53,6 @@ class SystemSettings(Document): frappe.cache().delete_value("system_settings") frappe.cache().delete_value("time_zone") - frappe.local.system_settings = {} if frappe.flags.update_last_reset_password_date: update_last_reset_password_date() From 248c3555e3537c874b448e7c62b2af7a5a6bc751 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Wed, 20 Apr 2022 15:16:44 +0530 Subject: [PATCH 44/45] fix(minor): add error_log for failed webhooks and web pages --- frappe/integrations/doctype/webhook/webhook.py | 2 +- frappe/public/scss/website/my_account.scss | 1 + frappe/website/serve.py | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/integrations/doctype/webhook/webhook.py b/frappe/integrations/doctype/webhook/webhook.py index 04a1c6c21e..3f1b60d903 100644 --- a/frappe/integrations/doctype/webhook/webhook.py +++ b/frappe/integrations/doctype/webhook/webhook.py @@ -105,7 +105,7 @@ def enqueue_webhook(doc, webhook): if i != 2: continue else: - raise e + webhook.log_error("Webhook failed") def log_request(url, headers, data, res): diff --git a/frappe/public/scss/website/my_account.scss b/frappe/public/scss/website/my_account.scss index 58075580a2..82c4164a31 100644 --- a/frappe/public/scss/website/my_account.scss +++ b/frappe/public/scss/website/my_account.scss @@ -30,6 +30,7 @@ .my-account-container { max-width: 800px; margin: auto; + margin-bottom: 4rem; } .account-info { diff --git a/frappe/website/serve.py b/frappe/website/serve.py index b30f3f1047..2c33b5df51 100644 --- a/frappe/website/serve.py +++ b/frappe/website/serve.py @@ -20,6 +20,7 @@ def get_response(path=None, http_status_code=200): except frappe.PermissionError as e: response = NotPermittedPage(endpoint, http_status_code, exception=e).render() except Exception as e: + frappe.log_error(f"{path} failed") response = ErrorPage(exception=e).render() return response From 296a6bd02f203d68b212991deec3702384943af2 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 20 Apr 2022 15:37:54 +0530 Subject: [PATCH 45/45] test: show locals on test failure (#16687) - Show locals always in CI - Show locals locally when running in verbose mode --- frappe/parallel_test_runner.py | 1 + frappe/test_runner.py | 15 +++++++++++---- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/frappe/parallel_test_runner.py b/frappe/parallel_test_runner.py index f5367e9dc6..65c8eb470b 100644 --- a/frappe/parallel_test_runner.py +++ b/frappe/parallel_test_runner.py @@ -117,6 +117,7 @@ class ParallelTestRunner: class ParallelTestResult(unittest.TextTestResult): def startTest(self, test): + self.tb_locals = True self._started_at = time.time() super(unittest.TextTestResult, self).startTest(test) test_class = unittest.util.strclass(test.__class__) diff --git a/frappe/test_runner.py b/frappe/test_runner.py index 5fa9f60197..509be36f86 100644 --- a/frappe/test_runner.py +++ b/frappe/test_runner.py @@ -15,6 +15,7 @@ import frappe import frappe.utils.scheduler from frappe.model.naming import revert_series_if_last from frappe.modules import get_module_name, load_doctype_module +from frappe.utils import cint unittest_runner = unittest.TextTestRunner SLOW_TEST_THRESHOLD = 2 @@ -177,10 +178,13 @@ def run_all_tests( _add_test(app, path, filename, verbose, test_suite, ui_tests) if junit_xml_output: - runner = unittest_runner(verbosity=1 + (verbose and 1 or 0), failfast=failfast) + runner = unittest_runner(verbosity=1 + cint(verbose), failfast=failfast) else: runner = unittest_runner( - resultclass=TimeLoggingTestResult, verbosity=1 + (verbose and 1 or 0), failfast=failfast + resultclass=TimeLoggingTestResult, + verbosity=1 + cint(verbose), + failfast=failfast, + tb_locals=verbose, ) if profile: @@ -279,10 +283,13 @@ def _run_unittest( test_suite.addTest(module_test_cases) if junit_xml_output: - runner = unittest_runner(verbosity=1 + (verbose and 1 or 0), failfast=failfast) + runner = unittest_runner(verbosity=1 + cint(verbose), failfast=failfast) else: runner = unittest_runner( - resultclass=TimeLoggingTestResult, verbosity=1 + (verbose and 1 or 0), failfast=failfast + resultclass=TimeLoggingTestResult, + verbosity=1 + cint(verbose), + failfast=failfast, + tb_locals=verbose, ) if profile: