Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0867b48
feat: camera implemented
lauty95 Nov 17, 2025
b34a73f
linters
lauty95 Nov 17, 2025
bc71d3f
Merge branch 'mobile_v15' into issue-175-photo
agritheory Dec 3, 2025
bf07bd5
test: camera basic tests
lauty95 Dec 10, 2025
bdac78e
linters
lauty95 Dec 10, 2025
c9d3db5
Merge branch 'issue-175-photo' of https://github.com/agritheory/beam …
lauty95 Dec 10, 2025
0e55175
test: attach photo to purchase receipt
lauty95 Dec 10, 2025
62346ec
linters
lauty95 Dec 10, 2025
2312239
fix: test button render onMounted component
lauty95 Dec 10, 2025
63633de
test: mark order
lauty95 Dec 10, 2025
c347c2d
test: import pytest
lauty95 Dec 10, 2025
28f6390
fix: onMounted camera validates
lauty95 Dec 10, 2025
827fa5f
test: wait for timeout
lauty95 Dec 10, 2025
d260cf3
feat: BEAM Mobile User permissions
lauty95 Feb 5, 2026
d463c56
fix: linters
lauty95 Feb 5, 2026
ab78617
logs
lauty95 Feb 6, 2026
d27ab9d
logs
lauty95 Feb 6, 2026
cd34cb6
feat: handle submit and cancel
lauty95 Feb 12, 2026
2f61299
fix: linters
lauty95 Feb 12, 2026
95feb17
fix: console.log
lauty95 Feb 12, 2026
721a230
feat: redirect implemented
lauty95 Feb 12, 2026
0bb5ce6
fix: linters
lauty95 Feb 12, 2026
bebbaaf
fix: margin
lauty95 Feb 12, 2026
fa105f2
fix: linters
lauty95 Feb 12, 2026
16a6b23
fix: time to redirect change to 1.5s
lauty95 Feb 12, 2026
f3a6db1
fix: doc event for purchase receipt
lauty95 Feb 12, 2026
8ac8a48
fix: linters
lauty95 Feb 12, 2026
07a1cf4
fix: linters
lauty95 Feb 13, 2026
675eb02
Merge branch 'mobile_v15' into issue-175-photo
lauty95 Jul 2, 2026
ad39a7c
fix: pre commit
lauty95 Jul 2, 2026
0683241
fix: resolve mypy and prettier lint failures
lauty95 Jul 2, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ repos:
rev: v2.4.1
hooks:
- id: codespell
args: ["--ignore-words-list", "notin"]
args: ['--ignore-words-list', 'notin']
exclude: 'yarn.lock|poetry.lock'
additional_dependencies:
- tomli
Expand Down Expand Up @@ -53,7 +53,7 @@ repos:
additional_dependencies: ['flake8-bugbear']

- repo: https://github.com/agritheory/test_utils
rev: v1.20.1
rev: v1.28.0
hooks:
- id: update_pre_commit_config
- id: validate_frappe_project
Expand Down
168 changes: 84 additions & 84 deletions beam/beam/custom/user.json
Original file line number Diff line number Diff line change
@@ -1,85 +1,85 @@
{
"custom_fields": [
{
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": null,
"dt": "User",
"fetch_if_empty": 0,
"fieldname": "barcode_section",
"fieldtype": "Section Break",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
"idx": 26,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
"insert_after": "mobile_no",
"label": "Barcodes",
"length": 0,
"module": "BEAM",
"name": "User-barcode_section",
"no_copy": 0,
"non_negative": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"translatable": 0,
"unique": 0
},
{
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": null,
"dt": "User",
"fetch_if_empty": 0,
"fieldname": "barcodes",
"fieldtype": "Table",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
"idx": 27,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
"insert_after": "barcode_section",
"length": 0,
"module": "BEAM",
"name": "User-barcodes",
"no_copy": 0,
"non_negative": 0,
"options": "Item Barcode",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"translatable": 0,
"unique": 0
}
],
"doctype": "User",
"property_setters": [],
"sync_on_migrate": 1
}
"custom_fields": [
{
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": null,
"dt": "User",
"fetch_if_empty": 0,
"fieldname": "barcode_section",
"fieldtype": "Section Break",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
"idx": 26,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
"insert_after": "mobile_no",
"label": "Barcodes",
"length": 0,
"module": "BEAM",
"name": "User-barcode_section",
"no_copy": 0,
"non_negative": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"translatable": 0,
"unique": 0
},
{
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": null,
"dt": "User",
"fetch_if_empty": 0,
"fieldname": "barcodes",
"fieldtype": "Table",
"hidden": 0,
"hide_border": 0,
"hide_days": 0,
"hide_seconds": 0,
"idx": 27,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_preview": 0,
"in_standard_filter": 0,
"insert_after": "barcode_section",
"length": 0,
"module": "BEAM",
"name": "User-barcodes",
"no_copy": 0,
"non_negative": 0,
"options": "Item Barcode",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"translatable": 0,
"unique": 0
}
],
"doctype": "User",
"property_setters": [],
"sync_on_migrate": 1
}
28 changes: 19 additions & 9 deletions beam/beam/demand/receiving.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright (c) 2024, AgriTheory and contributors
# For license information, please see license.txt

from typing import TYPE_CHECKING, Optional, Union
from typing import TYPE_CHECKING, Optional

import frappe
from frappe.query_builder import DocType
Expand All @@ -21,9 +21,6 @@
if TYPE_CHECKING:
from sqlite3 import Cursor

from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import PurchaseInvoice
from erpnext.buying.doctype.purchase_order.purchase_order import PurchaseOrder


def _get_receiving_demand(
name: str | None = None, item_code: str | None = None
Expand Down Expand Up @@ -139,13 +136,24 @@ def _get_receiving_demand(


@validate_demand_enabled
def modify_receiving(
doc: Union["PurchaseOrder", "PurchaseInvoice"], method: str | None = None
) -> None:
def modify_receiving(doc, method: str | None = None) -> None:
"""Update receiving table for Purchase Orders, Purchase Invoices, and Purchase Receipts"""
if method == "on_submit":
add_receiving(doc.name)
if doc.doctype == "Purchase Receipt":
purchase_orders = {item.purchase_order for item in doc.items if item.purchase_order}
for po_name in purchase_orders:
remove_receiving(po_name)
add_receiving(po_name)
else:
add_receiving(doc.name)
elif method == "on_cancel":
remove_receiving(doc.name)
if doc.doctype == "Purchase Receipt":
purchase_orders = {item.purchase_order for item in doc.items if item.purchase_order}
for po_name in purchase_orders:
remove_receiving(po_name)
add_receiving(po_name)
else:
remove_receiving(doc.name)


def add_receiving(name: str) -> None:
Expand Down Expand Up @@ -282,6 +290,8 @@ def get_receiving_demand(*args, **kwargs) -> list[Receiving]:
for r_filter in r_filters:
receiving_query = receiving_query.where(*r_filter)

receiving_query = receiving_query.where(receiving.received_qty < receiving.stock_qty)

record_offset = records_per_page * (page - 1)

query = f"{receiving_query} LIMIT {records_per_page} OFFSET {record_offset}"
Expand Down
3 changes: 2 additions & 1 deletion beam/beam/demand/sqlite.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import pathlib
import sqlite3
from typing import Any

import frappe
from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions
Expand Down Expand Up @@ -153,7 +154,7 @@ def reset_receiving_db() -> None:
cursor.execute("DELETE FROM receiving")


def dict_factory(cursor: sqlite3.Cursor, row: sqlite3.Row) -> frappe._dict:
def dict_factory(cursor: sqlite3.Cursor, row: tuple[Any, ...]) -> frappe._dict:
_dict = frappe._dict()
for idx, col in enumerate(cursor.description):
_dict[col[0]] = row[idx]
Expand Down
4 changes: 4 additions & 0 deletions beam/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@
"on_submit": ["beam.beam.demand.receiving.modify_receiving"],
"on_cancel": ["beam.beam.demand.receiving.modify_receiving"],
},
"Purchase Receipt": {
"on_submit": ["beam.beam.demand.receiving.modify_receiving"],
"on_cancel": ["beam.beam.demand.receiving.modify_receiving"],
},
"Purchase Invoice": {
"on_submit": [
"beam.beam.demand.receiving.modify_receiving",
Expand Down
76 changes: 76 additions & 0 deletions beam/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,81 @@ def create_beam_mobile_user_role():
role.insert(ignore_permissions=True)


def setup_beam_mobile_user_permissions():
"""Grant necessary permissions to BEAM Mobile User role for mobile app functionality"""
role = "BEAM Mobile User"

# Core doctypes - READ only (for reference data)
read_only_doctypes = [
"Address",
"Contact",
"Company",
"Currency",
"Customer",
"Supplier",
"Item",
"Warehouse",
"UOM",
"Price List",
"Item Price",
"Batch",
"Serial No",
"Item Group",
"Brand",
"UOM Conversion Detail",
]

# Source doctypes - READ only (for mapping to new documents)
source_doctypes = [
"Purchase Order",
"Sales Order",
"Work Order",
]

# Target doctypes - Full CRUD permissions
crud_doctypes = [
"Purchase Receipt",
"Delivery Note",
"Stock Entry",
]

# Add READ permissions
for doctype in read_only_doctypes + source_doctypes:
if not frappe.db.exists("Custom DocPerm", {"parent": doctype, "role": role, "permlevel": 0}):
frappe.get_doc(
{
"doctype": "Custom DocPerm",
"parent": doctype,
"parenttype": "DocType",
"parentfield": "permissions",
"role": role,
"read": 1,
"permlevel": 0,
}
).insert(ignore_permissions=True)

# Add CRUD permissions for transactional doctypes
for doctype in crud_doctypes:
if not frappe.db.exists("Custom DocPerm", {"parent": doctype, "role": role, "permlevel": 0}):
frappe.get_doc(
{
"doctype": "Custom DocPerm",
"parent": doctype,
"parenttype": "DocType",
"parentfield": "permissions",
"role": role,
"read": 1,
"write": 1,
"create": 1,
"submit": 1,
"cancel": 1,
"permlevel": 0,
}
).insert(ignore_permissions=True)

frappe.db.commit()


def after_install():
print("Setting up Handling Unit Inventory Dimension")
if frappe.db.exists("Inventory Dimension", "Handling Unit"):
Expand Down Expand Up @@ -61,4 +136,5 @@ def after_install():
build_demand_allocation_map()
reset_build_receiving_map()
create_beam_mobile_user_role()
setup_beam_mobile_user_permissions()
execute()
Loading
Loading