Skip to content

mhbu50/master_detail_grid

Repository files navigation

Master Detail Grid

A comprehensive solution for creating advanced master-detail views in Frappe/ERPNext using DevExpress DataGrid and TreeList components. This app provides powerful grid features including expandable detail rows, nested grids, tabbed content, filtering, grouping, and much more.


πŸ“‹ Table of Contents


✨ Features

Core Features

  • βœ… Master-Detail Grid: Expandable rows with nested detail views
  • βœ… TreeList Support: Hierarchical data with parent-child relationships
  • βœ… Tabbed Detail Views: Multiple tabs in expanded detail section
  • βœ… Nested Grids: Display related child tables within detail rows
  • βœ… Inline Editing: Edit data directly in the grid with validation
  • βœ… Advanced Filtering: Column filters, search panel, filter rows
  • βœ… Grouping & Sorting: Multi-column grouping and sorting
  • βœ… Data Export: Export to Excel/CSV with formatting
  • βœ… Column Management: Show/hide, resize, reorder columns
  • βœ… Responsive Design: Mobile-friendly layouts
  • βœ… Real-time Sync: Changes sync with Frappe backend automatically

DevExpress Components

  • βœ… dxDataGrid: Advanced data grid with rich features
  • βœ… dxTreeList: Hierarchical tree-based data grid
  • βœ… Master-Detail Pattern: Expandable rows with custom detail templates

Integration Features

  • βœ… Work Bundle Integration: Add Work Bundle items to Project WBS
  • βœ… Dynamic Configuration: JSON-based configuration system
  • βœ… Permission-Aware: Respects Frappe permissions
  • βœ… Multi-CDN Loader: Automatic fallback for DevExpress library loading

πŸ“¦ Installation

Prerequisites

  • Frappe Framework v14 or higher
  • ERPNext (optional, for demo features)
  • Internet connection (for CDN libraries)

Method 1: Using Bench CLI (Production)

# Navigate to your bench directory
cd /path/to/frappe-bench

# Get the app
bench get-app https://github.com/your-org/master_detail_grid.git

# Install on your site
bench --site your-site.local install-app master_detail_grid

# Run migrations
bench --site your-site.local migrate

# Build assets
bench build --app master_detail_grid

# Restart
bench restart

Method 2: Development Setup

# Clone into apps directory
cd frappe-bench/apps
git clone https://github.com/your-org/master_detail_grid.git

# Install on site
bench --site your-site.local install-app master_detail_grid

# Migrate
bench --site your-site.local migrate

# Build and restart
bench build --app master_detail_grid
bench restart

Verify Installation

# Check if app is installed
bench --site your-site.local console

# In Python console:
>>> import master_detail_grid
>>> print(master_detail_grid.__version__)

πŸš€ Quick Start

Try the Demo

After installation, explore the demo DocTypes:

  1. Sales Order Demo

    • Navigate to: Sales Order Demo list
    • Create new document
    • See master-detail grid in action
  2. Project WBS

    • Navigate to: Project WBS list
    • Create/open a document
    • See nested master-detail grids with tabs

Basic Usage

Step 1: Add HTML field to your DocType

In DocType customization:

  • Add a new field
  • Field Type: HTML
  • Fieldname: master_detail_grid
  • Options: Configuration JSON (see below)

Step 2: Configure the field

Add this JSON to the HTML field's Options:

{
  "child_doctype": "Your Child DocType",
  "child_table_field": "your_child_table_fieldname",
  "detail_tabs": [
    {
      "title": "Details",
      "type": "details",
      "fields": ["field1", "field2", "field3"]
    }
  ]
}

Step 3: Save and reload

  • Save the DocType
  • Reload the form
  • The master-detail grid will appear

βš™οΈ Configuration

Master Detail Grid Config

The app provides a Master Detail Grid Config DocType for advanced, reusable configurations.

Creating a Configuration

  1. Navigate to: Master Detail Grid Config list
  2. Click New
  3. Fill in configuration details

Configuration Fields

Field Type Description Required
Config Name Data Unique identifier for this configuration βœ… Yes
Description Text Purpose/usage of this configuration No
Parent DocType Link The parent DocType (e.g., "Project WBS") βœ… Yes
Child DocType Link The child DocType (e.g., "WBS Grid") βœ… Yes
Parent Field Data Parent link field in child table βœ… Yes
Grid Type Select TreeList or DataGrid βœ… Yes

Grid Type Options:

  • TreeList: Use for hierarchical data with parent-child relationships
  • DataGrid: Use for flat tabular data

Tab Configuration

Add tabs to define what appears in the expanded detail row:

Field Description
Tab Title Display name of the tab
Tab Index Order of tabs (0-based)
Source DocType DocType to fetch data from
Grid Filter Field Field to filter by (e.g., "rfp")
Grid Filter Value Parent field value to use for filtering
Filter Configuration Additional JSON filter (optional)

Filter Configuration Example:

{
  "field": "category",
  "value": "Materials"
}

This filters the nested grid to only show rows where category = "Materials".

Using Master Detail Grid Config

Method 1: Reference by Name

In your DocType's HTML field options:

{
  "config_name": "project_wbs_grid"
}

Method 2: Inline Configuration

{
  "child_doctype": "WBS Grid",
  "child_table_field": "wbs_grid",
  "grid_type": "TreeList",
  "detail_tabs": [...]
}

Field Configuration

Configure which fields appear in the grid and how they behave.

Basic Field Configuration

{
  "child_doctype": "Sales Order Item",
  "child_table_field": "items",
  "columns": [
    {
      "dataField": "item_code",
      "caption": "Item Code",
      "dataType": "string",
      "width": 150,
      "allowEditing": true,
      "allowFiltering": true,
      "allowSorting": true
    },
    {
      "dataField": "qty",
      "caption": "Quantity",
      "dataType": "number",
      "format": "#,##0.##",
      "width": 100,
      "allowEditing": true
    },
    {
      "dataField": "rate",
      "caption": "Rate",
      "dataType": "number",
      "format": "currency",
      "width": 120,
      "allowEditing": true
    }
  ]
}

Field Properties

Property Type Description Default
dataField String Field name from child DocType Required
caption String Column header text Field label
dataType String Data type: string, number, date, boolean Auto-detect
width Number Column width in pixels Auto
allowEditing Boolean Allow inline editing true
allowFiltering Boolean Show filter in header true
allowSorting Boolean Allow column sorting true
allowGrouping Boolean Allow grouping by this column true
visible Boolean Show/hide column true
format String Display format (numbers, dates) None
alignment String left, right, center Auto
fixed Boolean Fix column position false
fixedPosition String left or right None

Data Type Formats

Numbers:

{
  "dataField": "amount",
  "format": "#,##0.00"  // 1,234.56
}

Currency:

{
  "dataField": "total",
  "format": "currency"  // Uses system currency
}

Dates:

{
  "dataField": "delivery_date",
  "format": "yyyy-MM-dd"  // 2025-11-05
}

Percentages:

{
  "dataField": "tax_rate",
  "format": "#0.##%"  // 15.5%
}

Tab Configuration

Define tabs that appear when a row is expanded.

Tab Types

1. Details Tab - Display field values

{
  "title": "Item Details",
  "type": "details",
  "fields": [
    "item_code",
    "item_name",
    "description",
    "quantity",
    "rate",
    "amount"
  ]
}

2. Info Tab - Static HTML content

{
  "title": "Help",
  "type": "info",
  "content": "<h4>Instructions</h4><p>Enter item details...</p>"
}

3. Grid Tab - Nested grid

{
  "title": "Materials",
  "type": "grid",
  "source_doctype": "WBS Grid",
  "grid_filter_field": "rfp",
  "grid_filter_value": "name",
  "filter": {
    "field": "category",
    "value": "Materials"
  }
}

Tab Properties

Property Type Description Required
title String Tab display name βœ… Yes
type String details, info, or grid βœ… Yes
fields Array Fields to display (details type) For details
content String HTML content (info type) For info
source_doctype String DocType for nested grid For grid
grid_filter_field String Field to filter nested grid For grid
grid_filter_value String Parent field for filter value For grid
filter Object Additional filter Optional

Filter Configuration

Apply filters to nested grids or main grid.

Simple Filter

{
  "field": "status",
  "value": "Active"
}

Complex Filter

{
  "field": "category",
  "operator": "in",
  "value": ["Materials", "Tools", "Equipment"]
}

Filter Operators

  • = or eq: Equals
  • != or ne: Not equals
  • > or gt: Greater than
  • < or lt: Less than
  • >= or gte: Greater than or equal
  • <= or lte: Less than or equal
  • in: In array
  • not in: Not in array
  • contains: String contains
  • startswith: String starts with
  • endswith: String ends with

πŸ“– Usage Examples

Example 1: Simple Sales Order Items

{
  "child_doctype": "Sales Order Item",
  "child_table_field": "items",
  "detail_tabs": [
    {
      "title": "Item Info",
      "type": "details",
      "fields": [
        "item_code",
        "item_name",
        "description",
        "qty",
        "rate",
        "amount"
      ]
    }
  ]
}

Example 2: Project WBS with Nested Grids

{
  "config_name": "project_wbs_grid",
  "child_doctype": "WBS Item",
  "child_table_field": "wbs_item",
  "grid_type": "TreeList",
  "parent_field": "parent_wbs_item",
  "detail_tabs": [
    {
      "title": "Materials",
      "type": "grid",
      "source_doctype": "WBS Grid",
      "grid_filter_field": "rfp",
      "grid_filter_value": "name",
      "filter": {
        "field": "category",
        "value": "Materials"
      }
    },
    {
      "title": "Tools",
      "type": "grid",
      "source_doctype": "WBS Grid",
      "grid_filter_field": "rfp",
      "grid_filter_value": "name",
      "filter": {
        "field": "category",
        "value": "Tools"
      }
    },
    {
      "title": "Equipment",
      "type": "grid",
      "source_doctype": "WBS Grid",
      "grid_filter_field": "rfp",
      "grid_filter_value": "name",
      "filter": {
        "field": "category",
        "value": "Equipment"
      }
    }
  ]
}

Example 3: Invoice with Multiple Detail Tabs

{
  "child_doctype": "Sales Invoice Item",
  "child_table_field": "items",
  "columns": [
    {
      "dataField": "item_code",
      "caption": "Item",
      "width": 150
    },
    {
      "dataField": "qty",
      "caption": "Quantity",
      "dataType": "number",
      "format": "#,##0.##",
      "width": 100
    },
    {
      "dataField": "rate",
      "caption": "Rate",
      "dataType": "number",
      "format": "currency",
      "width": 120
    },
    {
      "dataField": "amount",
      "caption": "Amount",
      "dataType": "number",
      "format": "currency",
      "width": 120,
      "allowEditing": false
    }
  ],
  "detail_tabs": [
    {
      "title": "Item Details",
      "type": "details",
      "fields": [
        "item_code",
        "item_name",
        "description",
        "image"
      ]
    },
    {
      "title": "Pricing",
      "type": "details",
      "fields": [
        "qty",
        "uom",
        "rate",
        "discount_percentage",
        "discount_amount",
        "amount"
      ]
    },
    {
      "title": "Accounting",
      "type": "details",
      "fields": [
        "income_account",
        "expense_account",
        "cost_center"
      ]
    },
    {
      "title": "Delivery",
      "type": "details",
      "fields": [
        "delivery_date",
        "warehouse",
        "actual_qty",
        "stock_uom"
      ]
    }
  ]
}

Example 4: BOM with Hierarchical Items

{
  "child_doctype": "BOM Item",
  "child_table_field": "items",
  "grid_type": "TreeList",
  "parent_field": "parent_bom_item",
  "columns": [
    {
      "dataField": "item_code",
      "caption": "Item Code",
      "width": 200
    },
    {
      "dataField": "qty",
      "caption": "Quantity",
      "dataType": "number",
      "format": "#,##0.####",
      "width": 100
    },
    {
      "dataField": "rate",
      "caption": "Rate",
      "dataType": "number",
      "format": "currency",
      "width": 120
    },
    {
      "dataField": "is_group",
      "caption": "Is Group",
      "dataType": "boolean",
      "width": 100
    }
  ],
  "detail_tabs": [
    {
      "title": "Item Info",
      "type": "details",
      "fields": [
        "item_code",
        "item_name",
        "description",
        "stock_uom",
        "qty",
        "rate",
        "amount"
      ]
    },
    {
      "title": "Operations",
      "type": "grid",
      "source_doctype": "BOM Operation",
      "grid_filter_field": "bom_item",
      "grid_filter_value": "name"
    }
  ]
}

πŸ”§ Advanced Features

TreeList Features

For hierarchical data (parent-child relationships):

{
  "grid_type": "TreeList",
  "parent_field": "parent_item",
  "key_field": "name",
  "parent_id_field": "parent_item",
  "has_items_field": "is_group"
}

TreeList Properties:

  • parent_field: Field linking to parent row
  • key_field: Unique identifier field
  • parent_id_field: Field containing parent ID
  • has_items_field: Boolean field indicating if row has children
  • expanded: Auto-expand rows (boolean or array of keys)

Master-Detail Row Toggle

// In custom script
// Toggle detail row programmatically
frm.fields_dict.master_detail_grid.grid_instance.toggle_detail_row(rowKey, rowData);

// Access opened row
let openedRow = window._current_opened_master_detail_row;

Custom Detail Templates

Define custom HTML templates for detail rows:

{
  "detail_template": "<div class='custom-detail'>{{item_name}}: {{description}}</div>"
}

Grid Refresh

Refresh grid data after updates:

// Refresh main grid
frm.fields_dict.master_detail_grid.grid_instance.refresh();

// Refresh nested grid
frm.fields_dict.master_detail_grid.refresh_nested_grid(gridId);

Export Options

Configure export behavior:

{
  "export": {
    "enabled": true,
    "formats": ["xlsx", "pdf"],
    "fileName": "export_{{docname}}"
  }
}

Paging and Scrolling

Configure pagination and virtual scrolling:

{
  "paging": {
    "enabled": true,
    "pageSize": 20,
    "pageSizes": [10, 20, 50, 100]
  },
  "scrolling": {
    "mode": "virtual",
    "rowRenderingMode": "virtual"
  }
}

πŸ“¦ Work Bundle Integration

The app includes special integration for Work Bundles in Project WBS.

Features

  • Add Work Bundle items to Project WBS
  • Select from Work Bundle libraries (Materials, Tools, Equipment, Technicians)
  • Filter and select specific items
  • Automatically creates WBS Grid records
  • Refreshes nested grid after insertion

Usage

  1. Open Project WBS document
  2. Expand RFP row in the master grid
  3. Click "Add Bundle" button
  4. Select Work Bundle from the list
  5. Choose category (Materials/Tools/Equipment/Technicians)
  6. Select items using checkboxes
  7. Click "Insert Selected Items"
  8. Items are added to WBS Grid and grid refreshes

API Methods

Get Work Bundles:

bundles = frappe.call(
    'master_detail_grid.master_detail_grid.doctype.project_wbs.project_wbs.get_work_bundles_for_project_wbs'
)

Get Work Bundle Items:

items = frappe.call(
    'master_detail_grid.master_detail_grid.doctype.project_wbs.project_wbs.get_work_bundle_items_for_project_wbs',
    bundle_name='WB-001'
)

Insert Items:

result = frappe.call(
    'master_detail_grid.master_detail_grid.doctype.project_wbs.project_wbs.insert_bundle_items_to_project_wbs',
    project_wbs_name='PWS-001',
    selected_items=items_json,
    parent_rfp_name='RFP-001'
)

πŸ“š API Reference

Python API

Get Child Table Data

from master_detail_grid.api import get_child_table_data

data = get_child_table_data(
    parent_doctype="Sales Order",
    parent_name="SO-2025-00001",
    child_table_field="items"
)

Add Child Table Row

from master_detail_grid.api import add_child_table_row

new_row = add_child_table_row(
    parent_doctype="Sales Order",
    parent_name="SO-2025-00001",
    child_table_field="items",
    child_doctype="Sales Order Item",
    data={
        "item_code": "ITEM-001",
        "qty": 10,
        "rate": 100
    }
)

Update Child Table Row

from master_detail_grid.api import update_child_table_row

updated_row = update_child_table_row(
    child_doctype="Sales Order Item",
    child_name="SOI-00001",
    data={
        "qty": 15,
        "rate": 120
    }
)

Delete Child Table Row

from master_detail_grid.api import delete_child_table_row

result = delete_child_table_row(
    child_doctype="Sales Order Item",
    child_name="SOI-00001"
)

JavaScript API

Access Grid Instance

// Get grid instance
let gridInstance = frm.fields_dict.master_detail_grid.grid_instance;

// Get DevExpress grid component
let dxGrid = gridInstance.getGridInstance();

Toggle Detail Row

// Toggle detail row
gridInstance.toggle_detail_row(rowKey, rowData);

// Show detail row
gridInstance.show_detail_row(rowKey, rowData);

// Remove detail row
gridInstance.remove_detail_row(rowKey);

Refresh Grid

// Refresh main grid
gridInstance.refresh();

// Refresh nested grid
gridInstance.refresh_nested_grid(gridId);

Get Selected Rows

// Get selected row keys
let selectedKeys = dxGrid.getSelectedRowKeys();

// Get selected row data
let selectedData = dxGrid.getSelectedRowsData();

Whitelisted Methods

All API methods are whitelisted and can be called via frappe.call():

frappe.call({
    method: 'master_detail_grid.api.get_child_table_data',
    args: {
        parent_doctype: 'Sales Order',
        parent_name: 'SO-2025-00001',
        child_table_field: 'items'
    },
    callback: function(r) {
        console.log(r.message);
    }
});

🎨 Customization

Custom CSS

Add custom styles in your app or custom app:

/* Custom grid container */
.master-detail-grid-container {
    border: 1px solid #e0e0e0;
    border-radius: 4px;
    padding: 10px;
}

/* Custom header */
.dx-datagrid-headers {
    background: #f8f9fa;
    font-weight: bold;
}

/* Custom detail row */
.dx-master-detail-row {
    background: #fafafa;
    padding: 20px;
}

/* Custom tab panel */
.dx-tabpanel {
    margin-top: 15px;
}

Custom JavaScript

Extend the grid component:

frappe.ui.form.on('Your DocType', {
    refresh: function(frm) {
        // Access grid instance
        let grid = frm.fields_dict.master_detail_grid.grid_instance;

        // Add custom toolbar button
        grid.addCustomButton('Custom Action', function() {
            // Your custom logic
        });

        // Listen to grid events
        grid.on('row_expanded', function(e) {
            console.log('Row expanded:', e.key);
        });
    }
});

Custom Field Rendering

Override field rendering in detail tabs:

frappe.ui.MasterDetailGrid.fieldRenderers = {
    'custom_field': function(value, row) {
        return `<span class="badge">${value}</span>`;
    }
};

Custom Detail Template

Provide custom template function:

frm.fields_dict.master_detail_grid.detailTemplate = function(rowData) {
    return `
        <div class="custom-detail-content">
            <h4>${rowData.item_name}</h4>
            <p>${rowData.description}</p>
            <ul>
                <li>Qty: ${rowData.qty}</li>
                <li>Rate: ${format_currency(rowData.rate)}</li>
            </ul>
        </div>
    `;
};

πŸ” Troubleshooting

Grid Not Displaying

Symptoms: HTML field shows empty or loading indicator

Solutions:

  1. Check browser console for JavaScript errors
  2. Verify DevExpress library loaded:
    console.log(typeof DevExpress);  // Should be "object"
  3. Clear cache and rebuild:
    bench build --app master_detail_grid
    bench --site your-site.local clear-cache
  4. Check field configuration JSON is valid
  5. Verify child DocType and field names are correct

DevExpress Not Loading

Symptoms: Console error about DevExpress not found

Solutions:

  1. Check internet connection (CDN libraries)
  2. Verify no firewall blocking CDN URLs
  3. Check devexpress_loader.js is included in hooks.py
  4. Try different CDN (loader has automatic fallback)
  5. Download DevExpress locally if CDN blocked

Check CDN Status:

curl -I https://cdn.jsdelivr.net/npm/devextreme@23.2.3/bundles/dx.all.js
# Should return HTTP 200

Data Not Syncing

Symptoms: Changes in grid don't save to backend

Solutions:

  1. Check form is saved (unsaved forms can't update child tables)
  2. Verify user has write permission on child DocType
  3. Check for validation errors in console
  4. Ensure child table field name matches configuration
  5. Check parent document is not submitted/cancelled

Nested Grids Not Showing

Symptoms: Detail row expands but nested grid is empty

Solutions:

  1. Verify source_doctype exists and is correct
  2. Check filter configuration matches data
  3. Verify data exists for the filter criteria
  4. Check console for errors
  5. Test filter query manually:
    frappe.get_all('WBS Grid', filters={'rfp': 'RFP-001', 'category': 'Materials'})

Performance Issues

Symptoms: Grid slow to load or laggy

Solutions:

  1. Enable virtual scrolling:
    {
      "scrolling": {
        "mode": "virtual",
        "rowRenderingMode": "virtual"
      }
    }
  2. Reduce page size:
    {
      "paging": {
        "pageSize": 20
      }
    }
  3. Limit visible columns
  4. Use server-side filtering
  5. Optimize child table queries

Styling Issues

Symptoms: Grid looks broken or misaligned

Solutions:

  1. Clear browser cache (Ctrl+Shift+Del)
  2. Rebuild with hard refresh:
    bench build --hard
  3. Check for CSS conflicts with other apps
  4. Verify DevExpress CSS loaded
  5. Test in incognito mode

Export Not Working

Symptoms: Export button doesn't work

Solutions:

  1. Check ExcelJS library loaded
  2. Verify export permissions
  3. Check browser allows downloads
  4. Look for console errors
  5. Try different export format

🌐 Browser Compatibility

Browser Version Support Notes
Chrome 90+ βœ… Full Recommended
Edge 90+ βœ… Full Chromium-based
Firefox 88+ βœ… Full All features work
Safari 14+ βœ… Full macOS/iOS
Opera 76+ βœ… Full Chromium-based
IE 11 - ❌ Not Supported Use modern browser

Mobile Support

  • βœ… Responsive Design: Adapts to mobile screens
  • βœ… Touch Support: Works with touch gestures
  • βœ… Mobile Browsers: iOS Safari, Chrome Android supported
  • ⚠️ Feature Limitation: Some advanced features (drag-drop) limited on mobile

πŸ“‹ Dependencies

Required

  • Frappe Framework: v14.0.0 or higher
  • DevExtreme: v23.2.3 (loaded via CDN)

Optional

  • ERPNext: For demo features and Work Bundle integration
  • ExcelJS: For Excel export (loaded via CDN)
  • FileSaver: For file download (loaded via CDN)

CDN Libraries

The app automatically loads these from CDN with fallback:

  1. DevExtreme

    • Primary: cdn.jsdelivr.net
    • Fallback: unpkg.com
    • Fallback: cdnjs.cloudflare.com
  2. ExcelJS (for export)

    • Primary: cdnjs.cloudflare.com
    • Fallback: unpkg.com
  3. FileSaver (for downloads)

    • Primary: cdnjs.cloudflare.com
    • Fallback: unpkg.com

🀝 Contributing

We welcome contributions! Please follow these guidelines:

Setup Development Environment

# Clone repository
git clone https://github.com/your-org/master_detail_grid.git
cd master_detail_grid

# Install pre-commit hooks
pre-commit install

# Make changes
# Run tests
# Commit with clear message

Code Quality Tools

This app uses pre-commit for code quality:

  • ruff: Python linting and formatting
  • eslint: JavaScript linting
  • prettier: Code formatting
  • pyupgrade: Python syntax modernization

Commit Guidelines

  • Use clear, descriptive commit messages
  • Follow conventional commits format
  • Reference issue numbers
  • Keep commits atomic

Pull Request Process

  1. Fork the repository
  2. Create feature branch
  3. Make changes with tests
  4. Ensure pre-commit passes
  5. Submit pull request
  6. Wait for review

πŸ“„ License

MIT License

Copyright (c) 2025 Accurate Systems

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


πŸ“ž Support


πŸ™ Acknowledgments

  • Frappe Framework team for the excellent framework
  • DevExpress for the powerful grid components
  • ERPNext community for feedback and contributions

Made with ❀️ by Accurate Systems

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors