diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ba218c..77b67e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,12 +46,12 @@ jobs: - name: Setup Python uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: "3.14" - name: Setup Node uses: actions/setup-node@v3 with: - node-version: 18 + node-version: 24 check-latest: true - name: Cache pip @@ -96,7 +96,7 @@ jobs: bench --site test_site install-app forms_pro bench build env: - CI: 'Yes' + CI: "Yes" - name: Run Tests working-directory: /home/runner/frappe-bench diff --git a/forms_pro/modules.txt b/forms_pro/modules.txt index ea7829e..f0d9322 100644 --- a/forms_pro/modules.txt +++ b/forms_pro/modules.txt @@ -1 +1,2 @@ -Forms Pro \ No newline at end of file +Forms Pro +User Forms \ No newline at end of file diff --git a/forms_pro/user_forms/__init__.py b/forms_pro/user_forms/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/forms_pro/utils/form_generator.py b/forms_pro/utils/form_generator.py index 28faeac..e570a9d 100644 --- a/forms_pro/utils/form_generator.py +++ b/forms_pro/utils/form_generator.py @@ -1,4 +1,6 @@ import frappe +from frappe import _ +from frappe.share import add_docshare FORMS_PRO_ROLE = "Forms Pro User" @@ -7,10 +9,19 @@ def create_form_with_doctype(team_id: str, doctype: str): roles = frappe.get_roles(frappe.session.user) if FORMS_PRO_ROLE not in roles: - frappe.throw("You are not authorized to create a form") - - form_generator = FormGenerator(team_id=team_id, linked_doctype=doctype) - form_generator.generate() + frappe.throw( + _("You are not authorized to create a form"), + frappe.PermissionError, + ) + + try: + form_generator = FormGenerator(team_id=team_id, linked_doctype=doctype) + form_generator.generate() + except Exception as e: + frappe.log_error(f"Error creating form with doctype: {doctype} - {e}") + frappe.throw( + _("Error creating form with doctype: {0} - {1}").format(doctype, e), + ) return { "doctype": form_generator.doctype.name, @@ -22,10 +33,19 @@ def create_form_with_doctype(team_id: str, doctype: str): def create_form(team_id: str): roles = frappe.get_roles(frappe.session.user) if FORMS_PRO_ROLE not in roles: - frappe.throw("You are not authorized to create a form") - - form_generator = FormGenerator(team_id=team_id) - form_generator.generate() + frappe.throw( + _("You are not authorized to create a form"), + frappe.PermissionError, + ) + + try: + form_generator = FormGenerator(team_id=team_id) + form_generator.generate() + except Exception as e: + frappe.log_error(f"Error creating form: {e}") + frappe.throw( + _("Error creating form: {0}").format(e), + ) return { "doctype": form_generator.doctype.name, @@ -47,6 +67,7 @@ def __init__( def generate(self) -> None: self._initialize_doctype() self._initialize_form_document() + frappe.clear_cache() def _initialize_doctype(self) -> None: if self.doctype: @@ -54,39 +75,74 @@ def _initialize_doctype(self) -> None: placeholder_doctype = frappe.new_doc("DocType") placeholder_doctype.name = self._generate_doctype_name() - placeholder_doctype.module = "Forms Pro" + placeholder_doctype.module = "User Forms" placeholder_doctype.custom = True placeholder_doctype.track_changes = True placeholder_doctype.track_views = True placeholder_doctype.index_web_pages_for_search = False - placeholder_doctype.insert(ignore_permissions=True) - - self.doctype = placeholder_doctype - # self.set_placeholder_doctype_fields() + placeholder_doctype.is_submittable = 0 + placeholder_doctype.istable = 0 + placeholder_doctype.editable_grid = 0 + placeholder_doctype.issingle = 0 + placeholder_doctype.is_tree = 0 + placeholder_doctype.is_virtual = 0 + + # Add permissions + placeholder_doctype.append( + "permissions", + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1, + "submit": 0, + }, + ) + + # Add fields + placeholder_doctype.append("fields", {"fieldtype": "Section Break"}) - # def set_placeholder_doctype_fields(self) -> None: - # fields = [ - # { - # "label": "Is Form Pro DocType", - # "fieldname": "is_forms_pro_doctype", - # "fieldtype": "Check", - # "reqd": 1, - # "read_only": 1, - # "default": 1, - # } - # ] + placeholder_doctype.insert(ignore_permissions=True) - # for field in fields: - # self.doctype.append("fields", field) + add_docshare( + "DocType", + placeholder_doctype.name, + read=1, + write=1, + share=1, + submit=0, + flags={ + "ignore_share_permission": True, + }, + ) - # self.doctype.save(ignore_permissions=True) + self.doctype = placeholder_doctype def _initialize_form_document(self) -> None: form_document = frappe.new_doc("Form") form_document.linked_doctype = self.doctype.name form_document.title = "Untitled Form" form_document.linked_team_id = self.team_id + form_document.document_type = "System" form_document.insert(ignore_permissions=True) + + add_docshare( + "Form", + form_document.name, + read=1, + write=1, + share=1, + submit=0, + flags={ + "ignore_share_permission": True, + }, + ) self.form_document = form_document def _generate_doctype_name(self) -> str: diff --git a/forms_pro/utils/test_form_generator.py b/forms_pro/utils/test_form_generator.py index 3ed680a..2998165 100644 --- a/forms_pro/utils/test_form_generator.py +++ b/forms_pro/utils/test_form_generator.py @@ -74,7 +74,7 @@ def test_generate_creates_doctype_when_none_provided(self): # Assertions self.assertIsNotNone(form_generator.doctype) self.assertTrue(form_generator.doctype.name.startswith("formspro_")) - self.assertEqual(form_generator.doctype.module, "Forms Pro") + self.assertEqual(form_generator.doctype.module, "User Forms") self.assertTrue(form_generator.doctype.custom) self.assertTrue(form_generator.doctype.track_changes) self.assertTrue(form_generator.doctype.track_views) @@ -133,7 +133,7 @@ def test_generate_complete_flow(self): # Verify DocType properties self.assertTrue(form_generator.doctype.name.startswith("formspro_")) - self.assertEqual(form_generator.doctype.module, "Forms Pro") + self.assertEqual(form_generator.doctype.module, "User Forms") self.assertTrue(form_generator.doctype.custom) def test_generate_with_existing_doctype_complete_flow(self): @@ -157,3 +157,57 @@ def test_generate_with_existing_doctype_complete_flow(self): # Verify Form document exists in database self.assertTrue(frappe.db.exists("Form", form_generator.form_document.name)) + + def test_generate_creates_doctype_docshare(self): + """Test that generate() creates DocShare for the DocType with correct permissions""" + frappe.set_user(self.test_user) + form_generator = FormGenerator(team_id=self.test_team) + + # Call generate method + form_generator.generate() + + # Query DocShare for the DocType + docshare = frappe.db.get_value( + "DocShare", + { + "share_doctype": "DocType", + "share_name": form_generator.doctype.name, + "user": self.test_user, + }, + ["read", "write", "share", "submit"], + as_dict=True, + ) + + # Assertions + self.assertIsNotNone(docshare, "DocShare should exist for DocType") + self.assertEqual(docshare.read, 1, "DocShare should have read permission") + self.assertEqual(docshare.write, 1, "DocShare should have write permission") + self.assertEqual(docshare.share, 1, "DocShare should have share permission") + self.assertEqual(docshare.submit, 0, "DocShare should not have submit permission") + + def test_generate_creates_form_docshare(self): + """Test that generate() creates DocShare for the Form document with correct permissions""" + frappe.set_user(self.test_user) + form_generator = FormGenerator(team_id=self.test_team) + + # Call generate method + form_generator.generate() + + # Query DocShare for the Form document + docshare = frappe.db.get_value( + "DocShare", + { + "share_doctype": "Form", + "share_name": form_generator.form_document.name, + "user": self.test_user, + }, + ["read", "write", "share", "submit"], + as_dict=True, + ) + + # Assertions + self.assertIsNotNone(docshare, "DocShare should exist for Form document") + self.assertEqual(docshare.read, 1, "DocShare should have read permission") + self.assertEqual(docshare.write, 1, "DocShare should have write permission") + self.assertEqual(docshare.share, 1, "DocShare should have share permission") + self.assertEqual(docshare.submit, 0, "DocShare should not have submit permission")