# Priority REST API - מדריך מקיף ומלא

> **מקורות רשמיים:**
> - [Priority Developer Portal](https://prioritysoftware.github.io/)
> - [REST API Documentation](https://prioritysoftware.github.io/restapi/)
> - [OData Protocol v4.0](http://www.odata.org/documentation/)
> - [Priority API PDF Guide](https://cdn.priority-software.com/docs/Priority_OData_API.pdf)

---

## תוכן עניינים

1. [הקדמה ומידע כללי](#1-הקדמה-ומידע-כללי)
2. [אימות (Authentication)](#2-אימות-authentication)
3. [מידע על השירות (Service Information)](#3-מידע-על-השירות-service-information)
4. [פעולות קריאה (Read Operations)](#4-פעולות-קריאה-read-operations)
5. [שאילתות וסינון (Query & Filter)](#5-שאילתות-וסינון-query--filter)
6. [פעולות יצירה (Create Operations)](#6-פעולות-יצירה-create-operations)
7. [פעולות עדכון (Update Operations)](#7-פעולות-עדכון-update-operations)
8. [פעולות מחיקה (Delete Operations)](#8-פעולות-מחיקה-delete-operations)
9. [פעולות אצווה (Batch Operations)](#9-פעולות-אצווה-batch-operations)
10. [Webhooks ואוטומציה](#10-webhooks-ואוטומציה)
11. [דוגמאות מעשיות מלאות](#11-דוגמאות-מעשיות-מלאות)

---

## 1. הקדמה ומידע כללי

### 1.1 מבנה URL בסיסי

```
https://{server}/odata/Priority/{config-file}/{environment}/{entity}
```

**דוגמה:**
```
https://www.eshbelsaas.com/odata/Priority/tabula.ini/mycompany/CUSTOMERS
```

**הסבר רכיבים:**
- `{server}` - כתובת שרת Priority
- `{config-file}` - קובץ הגדרות (בדרך כלל `tabula.ini`)
- `{environment}` - שם החברה/סביבה במערכת
- `{entity}` - שם ישות/טופס (CUSTOMERS, ORDERS וכו')

### 1.2 Headers נדרשים

```http
Authorization: Basic {base64-encoded-credentials}
Content-Type: application/json
Accept: application/json
OData-Version: 4.0
```

### 1.3 סביבת Demo לבדיקות

**פרטי גישה חופשיים:**
```
URL: https://t.eu.priority-connect.online/odata/Priority/tabbtd38.ini/usdemo
Username: apidemo
Password: 123
Version: 25.0
```

**דוגמת בדיקת חיבור:**
```bash
curl -X GET "https://t.eu.priority-connect.online/odata/Priority/tabbtd38.ini/usdemo/CUSTOMERS" \
  -u "apidemo:123" \
  -H "Accept: application/json"
```

---

## 2. אימות (Authentication)

### 2.1 Basic Authentication

**יצירת Authorization Header:**

```javascript
// JavaScript/Node.js
const username = 'apiuser';
const password = 'mypassword';
const credentials = Buffer.from(`${username}:${password}`).toString('base64');
const authHeader = `Basic ${credentials}`;
```

```python
# Python
import base64
username = 'apiuser'
password = 'mypassword'
credentials = base64.b64encode(f'{username}:{password}'.encode()).decode()
auth_header = f'Basic {credentials}'
```

**דוגמת שימוש:**
```bash
curl -X GET "https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS" \
  -H "Authorization: Basic YXBpdXNlcjpteXBhc3N3b3Jk" \
  -H "Accept: application/json"
```

### 2.2 Personal Access Token (PAT)

**יצירת PAT במערכת Priority:**
1. פתח טופס "REST Interface Access Tokens"
2. לחץ על "New Token"
3. העתק את הטוקן מיד (לא ניתן לראותו שוב!)

**שימוש ב-PAT:**

```javascript
const token = 'd6d4545ce5ab4827bb51ca3c03005d1d';
const credentials = Buffer.from(`${token}:PAT`).toString('base64');
const authHeader = `Basic ${credentials}`;
```

```bash
curl -X GET "https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS" \
  -H "Authorization: Basic ZDZkNDU0NWNlNWFiNDgyN2JiNTFjYTNjMDMwMDVkMWQ6UEFU" \
  -H "Accept: application/json"
```

**יתרונות PAT:**
- ניתן לבטל בלי לשנות סיסמה
- מעקב טוב יותר אחר שימוש
- אבטחה משופרת
- תמיכה במספר אפליקציות

### 2.3 OAuth2 Authentication

**דרישות מוקדמות:**
- רכישת מודול External ID
- הגדרת אפליקציה ב-Priority UI

**שלב 1 - רישום אפליקציה:**

נווט ל:
```
System Management > System Maintenance > Users > 
Manage IDs Externally > External Applications
```

תקבל:
- Application ID (Client ID)
- Secret ID (Client Secret)

**שלב 2 - Authorization Request:**

```http
GET https://{PRIORITY_DOMAIN}/accounts/connect/authorize?
    response_type=code
    &client_id={YOUR_CLIENT_ID}
    &redirect_uri={YOUR_REDIRECT_URL}
    &scope=openid rest_api
    &code_challenge={CODE_CHALLENGE}
    &code_challenge_method=S256
```

**שלב 3 - Exchange Token:**

```bash
curl -X POST "https://{PRIORITY_DOMAIN}/accounts/connect/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "Authorization: Basic {base64(client_id:client_secret)}" \
  -d "grant_type=authorization_code" \
  -d "code={AUTHORIZATION_CODE}" \
  -d "redirect_uri={YOUR_REDIRECT_URL}" \
  -d "code_verifier={CODE_VERIFIER}" \
  -d "client_id={YOUR_CLIENT_ID}"
```

**שלב 4 - שימוש ב-Access Token:**

```bash
curl -X GET "https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS" \
  -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjE2NzYyM..." \
  -H "Accept: application/json"
```

**OpenID Discovery:**
```bash
curl "https://{PRIORITY_DOMAIN}/accounts/.well-known/openid-configuration"
```

---

## 3. מידע על השירות (Service Information)

### 3.1 קבלת רשימת Entities זמינים

**Request:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/
Authorization: Basic {credentials}
Accept: application/json
```

**cURL:**
```bash
curl -X GET "https://t.eu.priority-connect.online/odata/Priority/tabbtd38.ini/usdemo/" \
  -u "apidemo:123" \
  -H "Accept: application/json"
```

**Response:**
```json
{
  "@odata.context": "https://server.com/odata/Priority/tabula.ini/company/$metadata",
  "value": [
    {
      "name": "ABILITIES",
      "kind": "EntitySet",
      "url": "ABILITIES"
    },
    {
      "name": "CUSTOMERS",
      "kind": "EntitySet",
      "url": "CUSTOMERS"
    },
    {
      "name": "ORDERS",
      "kind": "EntitySet",
      "url": "ORDERS"
    },
    {
      "name": "LOGPART",
      "kind": "EntitySet",
      "url": "LOGPART"
    }
  ]
}
```

### 3.2 קבלת Metadata מלא של המערכת

**Request:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/$metadata
Authorization: Basic {credentials}
Accept: application/xml
```

**cURL:**
```bash
curl -X GET "https://t.eu.priority-connect.online/odata/Priority/tabbtd38.ini/usdemo/\$metadata" \
  -u "apidemo:123" \
  -H "Accept: application/xml"
```

**Response (קטע):**
```xml
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
  <edmx:DataServices>
    <Schema Namespace="Priority.OData" xmlns="http://docs.oasis-open.org/odata/ns/edm">
      <EntityType Name="CUSTOMERS">
        <Key>
          <PropertyRef Name="CUSTNAME" />
        </Key>
        <Property Name="CUSTNAME" Type="Edm.String" MaxLength="16">
          <Annotation Term="Priority.OData.Description" String="Customer Number" />
        </Property>
        <Property Name="CUSTDES" Type="Edm.String" MaxLength="48">
          <Annotation Term="Priority.OData.Description" String="Customer Name" />
        </Property>
        <Property Name="EMAIL" Type="Edm.String" MaxLength="48" />
        <Property Name="PHONE" Type="Edm.String" MaxLength="20" />
        <NavigationProperty Name="ORDERS_SUBFORM" 
                          Type="Collection(Priority.OData.ORDERS)" 
                          ContainsTarget="true" />
      </EntityType>
    </Schema>
  </edmx:DataServices>
</edmx:Edmx>
```

### 3.3 קבלת Metadata של Entity ספציפי (v23.0+)

**Request:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/$metadata(CUSTOMERS)
Authorization: Basic {credentials}
Accept: application/xml
```

**cURL:**
```bash
curl -X GET "https://server.com/odata/Priority/tabula.ini/company/\$metadata(CUSTOMERS)" \
  -u "apiuser:password" \
  -H "Accept: application/xml"
```

**יתרון:** מהיר יותר - מקבל רק את המטא-דאטה של הישות הספציפית

### 3.4 בדיקת גרסת Priority

**Request:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/GetPriorityVersion()
Authorization: Basic {credentials}
Accept: application/json
```

**cURL:**
```bash
curl -X GET "https://t.eu.priority-connect.online/odata/Priority/tabbtd38.ini/usdemo/GetPriorityVersion()" \
  -u "apidemo:123" \
  -H "Accept: application/json"
```

**Response:**
```json
{
  "@odata.context": "https://server.com/odata/Priority/tabula.ini/company/$metadata#Edm.String",
  "value": "25.0-20.0.0.119"
}
```

**שימוש בקוד:**
```javascript
async function checkVersion() {
  const response = await fetch(
    'https://server.com/odata/Priority/tabula.ini/company/GetPriorityVersion()',
    {
      headers: {
        'Authorization': 'Basic ' + btoa('apiuser:password'),
        'Accept': 'application/json'
      }
    }
  );
  
  const data = await response.json();
  const version = parseFloat(data.value.split('-')[0]);
  
  console.log(`Priority Version: ${version}`);
  
  if (version >= 23.0) {
    console.log('Supports $metadata(ENTITY)');
  }
  if (version >= 22.0) {
    console.log('Supports ClearEntityMetadata');
  }
  if (version >= 21.0) {
    console.log('Supports Attachments as Data URI');
  }
}
```

---

## 4. פעולות קריאה (Read Operations)

### 4.1 קבלת רשימת רשומות (Get Collection)

**Request:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS
Authorization: Basic {credentials}
Accept: application/json
```

**cURL:**
```bash
curl -X GET "https://t.eu.priority-connect.online/odata/Priority/tabbtd38.ini/usdemo/CUSTOMERS" \
  -u "apidemo:123" \
  -H "Accept: application/json"
```

**Response:**
```json
{
  "@odata.context": "https://server.com/$metadata#CUSTOMERS",
  "value": [
    {
      "CUSTNAME": "C001",
      "CUSTDES": "ACME Corporation",
      "EMAIL": "[email protected]",
      "PHONE": "+1-555-0100",
      "ADDRESS": "123 Main St",
      "STATENAME": "NY"
    },
    {
      "CUSTNAME": "C002",
      "CUSTDES": "TechCorp Ltd",
      "EMAIL": "[email protected]",
      "PHONE": "+1-555-0200",
      "ADDRESS": "456 Tech Ave",
      "STATENAME": "CA"
    }
  ]
}
```

### 4.2 קבלת רשומה בודדת לפי מזהה

**Request:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS('C001')
Authorization: Basic {credentials}
Accept: application/json
```

**cURL:**
```bash
curl -X GET "https://t.eu.priority-connect.online/odata/Priority/tabbtd38.ini/usdemo/CUSTOMERS('T000001')" \
  -u "apidemo:123" \
  -H "Accept: application/json"
```

**Response:**
```json
{
  "@odata.context": "https://server.com/$metadata#CUSTOMERS/$entity",
  "CUSTNAME": "C001",
  "CUSTDES": "ACME Corporation",
  "EMAIL": "[email protected]",
  "PHONE": "+1-555-0100",
  "ADDRESS": "123 Main St",
  "CITY": "New York",
  "STATENAME": "NY",
  "ZIP": "10001"
}
```

### 4.3 קבלת רשומה עם Composite Key

**דוגמה לחשבוניות:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/AINVOICES(IVNUM='INV001',IVTYPE='A',DEBIT='D')
Authorization: Basic {credentials}
Accept: application/json
```

**cURL:**
```bash
curl -X GET "https://server.com/odata/Priority/tabula.ini/company/AINVOICES(IVNUM='INV001',IVTYPE='A',DEBIT='D')" \
  -u "apiuser:password" \
  -H "Accept: application/json"
```

### 4.4 קבלת נתונים מטבלת משנה (Subform)

**Request:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/ORDERS('SO001')/ORDERITEMS_SUBFORM
Authorization: Basic {credentials}
Accept: application/json
```

**cURL:**
```bash
curl -X GET "https://t.eu.priority-connect.online/odata/Priority/tabbtd38.ini/usdemo/ORDERS('SO17000003')/ORDERITEMS_SUBFORM" \
  -u "apidemo:123" \
  -H "Accept: application/json"
```

**Response:**
```json
{
  "@odata.context": "https://server.com/$metadata#ORDERS('SO001')/ORDERITEMS_SUBFORM",
  "value": [
    {
      "KLINE": 1,
      "PARTNAME": "ITEM001",
      "PDES": "Product 1",
      "TQUANT": 5,
      "PRICE": 100.00,
      "TOTPRICE": 500.00
    },
    {
      "KLINE": 2,
      "PARTNAME": "ITEM002",
      "PDES": "Product 2",
      "TQUANT": 3,
      "PRICE": 150.00,
      "TOTPRICE": 450.00
    }
  ]
}
```

### 4.5 קבלת טקסט HTML

**Request:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/ORDERS('SO001')/ORDERSTEXT_SUBFORM
Authorization: Basic {credentials}
Accept: application/json
```

**cURL:**
```bash
curl -X GET "https://server.com/odata/Priority/tabula.ini/company/ORDERS('SO001')/ORDERSTEXT_SUBFORM" \
  -u "apiuser:password" \
  -H "Accept: application/json"
```

**Response:**
```json
{
  "@odata.context": "https://server.com/$metadata#ORDERS('SO001')/ORDERSTEXT_SUBFORM/$entity",
  "TEXT": "<p>This is the order notes.</p><p>Please ship ASAP.</p>",
  "APPEND": null,
  "SIGNATURE": null
}
```

### 4.6 קבלת קבצים מצורפים

**Request:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/ORDERS('SO001')?$expand=EXTFILES_SUBFORM
Authorization: Basic {credentials}
Accept: application/json
```

**cURL:**
```bash
curl -X GET "https://server.com/odata/Priority/tabula.ini/company/ORDERS('SO001')?\$expand=EXTFILES_SUBFORM" \
  -u "apiuser:password" \
  -H "Accept: application/json"
```

**Response:**
```json
{
  "@odata.context": "https://server.com/$metadata#ORDERS/$entity",
  "ORDNAME": "SO001",
  "CUSTNAME": "C001",
  "EXTFILES_SUBFORM": [
    {
      "EXTFILENUM": 1,
      "EXTFILEDES": "Order Confirmation",
      "EXTFILENAME": "data:application/pdf;base64,JVBERi0xLjQKJeLjz9MK...",
      "SUFFIX": "pdf",
      "FILESIZE": 45632,
      "CURDATE": "2024-10-19T10:30:00+02:00"
    }
  ]
}
```

---

## 5. שאילתות וסינון (Query & Filter)

### 5.1 סינון פשוט (Simple Filter)

**דוגמה 1 - שוויון:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS?$filter=STATENAME eq 'CA'
Authorization: Basic {credentials}
Accept: application/json
```

**cURL:**
```bash
curl -X GET "https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS?\$filter=STATENAME%20eq%20'CA'" \
  -u "apiuser:password" \
  -H "Accept: application/json"
```

**דוגמה 2 - גדול מ:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/LOGPART?$filter=LASTPRICE gt 100
```

**דוגמה 3 - קטן או שווה:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/ORDERS?$filter=TOTPRICE le 1000
```

### 5.2 סינון עם אופרטורים לוגיים

**AND:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/LOGPART?$filter=TYPE eq 'P' and LASTPRICE gt 100 and ONHAND lt 50
```

**cURL:**
```bash
curl -X GET "https://server.com/odata/Priority/tabula.ini/company/LOGPART?\$filter=TYPE%20eq%20'P'%20and%20LASTPRICE%20gt%20100" \
  -u "apiuser:password" \
  -H "Accept: application/json"
```

**OR:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS?$filter=STATENAME eq 'CA' or STATENAME eq 'NY'
```

**AND + OR עם סוגריים:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/LOGPART?$filter=(TYPE eq 'P' or TYPE eq 'R') and LASTPRICE gt 500
```

### 5.3 סינון לפי תאריכים

**פורמט נכון:**
```
YYYY-MM-DDTHH:mm:ss+TZ
```

**דוגמה:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/ORDERS?$filter=CURDATE ge 2024-01-01T00:00:00%2B02:00
```

**טווח תאריכים:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/ORDERS?$filter=CURDATE ge 2024-01-01T00:00:00%2B02:00 and CURDATE le 2024-12-31T23:59:59%2B02:00
```

**cURL:**
```bash
curl -X GET "https://server.com/odata/Priority/tabula.ini/company/ORDERS?\$filter=CURDATE%20ge%202024-01-01T00:00:00%2B02:00" \
  -u "apiuser:password" \
  -H "Accept: application/json"
```

**שים לב:**
- רווח = `%20`
- פלוס (+) = `%2B`

### 5.4 שינויים מאז תאריך ($since)

**דרישה:** Entity חייב להיות עם BPM

**Request:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/ORDERS?$since=2024-10-01T00:00:00Z
Authorization: Basic {credentials}
Accept: application/json
```

**cURL:**
```bash
curl -X GET "https://server.com/odata/Priority/tabula.ini/company/ORDERS?\$since=2024-10-01T00:00:00Z" \
  -u "apiuser:password" \
  -H "Accept: application/json"
```

**עם הרחבת subforms:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/ORDERS?$since=2024-10-01T00:00:00Z&$expand=ORDERITEMS_SUBFORM
```

### 5.5 מיון (Order By)

**מיון עולה:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS?$orderby=CUSTDES asc
```

**מיון יורד:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/ORDERS?$orderby=CURDATE desc
```

**cURL:**
```bash
curl -X GET "https://server.com/odata/Priority/tabula.ini/company/ORDERS?\$orderby=CURDATE%20desc" \
  -u "apiuser:password" \
  -H "Accept: application/json"
```

**מיון לפי מספר שדות:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS?$orderby=STATENAME asc,CUSTDES asc
```

### 5.6 הגבלת תוצאות (Top & Skip)

**Top - מגביל מספר תוצאות:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS?$top=10
```

**Skip - דילוג על רשומות:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS?$skip=20
```

**Pagination (הדפדוף):**
```http
GET https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS?$top=50&$skip=100
```

**cURL - עמוד 3 (רשומות 101-150):**
```bash
curl -X GET "https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS?\$top=50&\$skip=100" \
  -u "apiuser:password" \
  -H "Accept: application/json"
```

### 5.7 בחירת שדות ($select)

**בחירה של שדות ספציפיים:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS?$select=CUSTNAME,CUSTDES,EMAIL,PHONE
```

**cURL:**
```bash
curl -X GET "https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS?\$select=CUSTNAME,CUSTDES,EMAIL" \
  -u "apiuser:password" \
  -H "Accept: application/json"
```

**Response:**
```json
{
  "@odata.context": "https://server.com/$metadata#CUSTOMERS(CUSTNAME,CUSTDES,EMAIL)",
  "value": [
    {
      "CUSTNAME": "C001",
      "CUSTDES": "ACME Corporation",
      "EMAIL": "[email protected]"
    }
  ]
}
```

**יתרון:** הקטנת גודל התשובה ושיפור ביצועים

### 5.8 הרחבת נתונים ($expand)

**הרחבה פשוטה:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/ORDERS('SO001')?$expand=ORDERITEMS_SUBFORM
```

**cURL:**
```bash
curl -X GET "https://server.com/odata/Priority/tabula.ini/company/ORDERS('SO001')?\$expand=ORDERITEMS_SUBFORM" \
  -u "apiuser:password" \
  -H "Accept: application/json"
```

**הרחבה מרובה:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/ORDERS('SO001')?$expand=ORDERITEMS_SUBFORM,ORDERSTEXT_SUBFORM,EXTFILES_SUBFORM
```

**הרחבה מקוננת (Nested):**
```http
GET https://server.com/odata/Priority/tabula.ini/company/ORDERS('SO001')?$expand=ORDERITEMS_SUBFORM($expand=ORDERITEMSTEXT_SUBFORM)
```

**הרחבה עם סינון:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/ORDERS?$expand=ORDERITEMS_SUBFORM($filter=PRICE gt 100;$select=PARTNAME,PRICE,TQUANT)
```

**שים לב:** בתוך $expand, הפרדת אופציות עם `;` או `%3B`

### 5.9 שאילתה משולבת מורכבת

**דוגמה מלאה:**
```http
GET https://server.com/odata/Priority/tabula.ini/company/ORDERS?
  $filter=CUSTNAME eq 'C001' and CURDATE ge 2024-01-01T00:00:00%2B02:00
  &$select=ORDNAME,CURDATE,TOTPRICE,ORDSTATUSDES
  &$expand=ORDERITEMS_SUBFORM($filter=PRICE gt 50;$select=PARTNAME,TQUANT,PRICE)
  &$orderby=CURDATE desc
  &$top=20
  &$skip=0
```

**cURL (מפוצל לשורות):**
```bash
curl -X GET "https://server.com/odata/Priority/tabula.ini/company/ORDERS?\
\$filter=CUSTNAME%20eq%20'C001'%20and%20CURDATE%20ge%202024-01-01T00:00:00%2B02:00\
&\$select=ORDNAME,CURDATE,TOTPRICE\
&\$expand=ORDERITEMS_SUBFORM(\$select=PARTNAME,TQUANT,PRICE)\
&\$orderby=CURDATE%20desc\
&\$top=20" \
  -u "apiuser:password" \
  -H "Accept: application/json"
```

---

## 6. פעולות יצירה (Create Operations)

### 6.1 יצירת רשומה בסיסית

**Request:**
```http
POST https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS
Authorization: Basic {credentials}
Content-Type: application/json
Accept: application/json
OData-Version: 4.0

{
  "CUSTNAME": "C999",
  "CUSTDES": "New Customer Ltd",
  "EMAIL": "[email protected]",
  "PHONE": "+1-555-9999"
}
```

**cURL:**
```bash
curl -X POST "https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS" \
  -u "apiuser:password" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "OData-Version: 4.0" \
  -d '{
    "CUSTNAME": "C999",
    "CUSTDES": "New Customer Ltd",
    "EMAIL": "[email protected]",
    "PHONE": "+1-555-9999"
  }'
```

**Response:**
```json
{
  "@odata.context": "https://server.com/$metadata#CUSTOMERS/$entity",
  "CUSTNAME": "C999",
  "CUSTDES": "New Customer Ltd",
  "EMAIL": "[email protected]",
  "PHONE": "+1-555-9999",
  "ADDRESS": null,
  "CITY": null,
  "STATENAME": null
}
```

### 6.2 יצירת רשומה בטבלת משנה

**Request:**
```http
POST https://server.com/odata/Priority/tabula.ini/company/ORDERS('SO001')/ORDERITEMS_SUBFORM
Authorization: Basic {credentials}
Content-Type: application/json
Accept: application/json

{
  "PARTNAME": "ITEM003",
  "TQUANT": 10,
  "DUEDATE": "2024-11-15T00:00:00+02:00"
}
```

**cURL:**
```bash
curl -X POST "https://server.com/odata/Priority/tabula.ini/company/ORDERS('SO001')/ORDERITEMS_SUBFORM" \
  -u "apiuser:password" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "PARTNAME": "ITEM003",
    "TQUANT": 10,
    "DUEDATE": "2024-11-15T00:00:00+02:00"
  }'
```

**Response:**
```json
{
  "@odata.context": "https://server.com/$metadata#ORDERS('SO001')/ORDERITEMS_SUBFORM/$entity",
  "KLINE": 3,
  "PARTNAME": "ITEM003",
  "PDES": "Product 3 Description",
  "TQUANT": 10,
  "TUNITNAME": "ea",
  "PRICE": 200.00,
  "TOTPRICE": 2000.00,
  "DUEDATE": "2024-11-15T00:00:00+02:00"
}
```

### 6.3 יצירת רשומה עם טבלאות משנה

**Request:**
```http
POST https://server.com/odata/Priority/tabula.ini/company/ORDERS
Authorization: Basic {credentials}
Content-Type: application/json
Accept: application/json

{
  "CUSTNAME": "C001",
  "ORDERITEMS_SUBFORM": [
    {
      "PARTNAME": "ITEM001",
      "TQUANT": 5,
      "DUEDATE": "2024-11-20T00:00:00+02:00"
    },
    {
      "PARTNAME": "ITEM002",
      "TQUANT": 3,
      "DUEDATE": "2024-11-20T00:00:00+02:00"
    }
  ]
}
```

**cURL:**
```bash
curl -X POST "https://server.com/odata/Priority/tabula.ini/company/ORDERS" \
  -u "apiuser:password" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "CUSTNAME": "C001",
    "ORDERITEMS_SUBFORM": [
      {
        "PARTNAME": "ITEM001",
        "TQUANT": 5,
        "DUEDATE": "2024-11-20T00:00:00+02:00"
      },
      {
        "PARTNAME": "ITEM002",
        "TQUANT": 3,
        "DUEDATE": "2024-11-20T00:00:00+02:00"
      }
    ]
  }'
```

**Response (v21.1+):**
```json
{
  "@odata.context": "https://server.com/$metadata#ORDERS(ORDERITEMS_SUBFORM())/$entity",
  "ORDNAME": "SO24001234",
  "CUSTNAME": "C001",
  "CDES": "ACME Corporation",
  "CURDATE": "2024-10-19T00:00:00+02:00",
  "ORDSTATUSDES": "Draft",
  "ORDERITEMS_SUBFORM": [
    {
      "KLINE": 1,
      "PARTNAME": "ITEM001",
      "PDES": "Product 1",
      "TQUANT": 5,
      "PRICE": 100.00,
      "DUEDATE": "2024-11-20T00:00:00+02:00"
    },
    {
      "KLINE": 2,
      "PARTNAME": "ITEM002",
      "PDES": "Product 2",
      "TQUANT": 3,
      "PRICE": 150.00,
      "DUEDATE": "2024-11-20T00:00:00+02:00"
    }
  ]
}
```

### 6.4 העלאת קובץ מצורף

**Request:**
```http
POST https://server.com/odata/Priority/tabula.ini/company/ORDERS('SO001')/EXTFILES_SUBFORM
Authorization: Basic {credentials}
Content-Type: application/json
Accept: application/json

{
  "EXTFILEDES": "Order Contract",
  "EXTFILENAME": "data:application/pdf;base64,JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFI+PgplbmRvYmoKMiAwIG9iago8PC9UeXBlL1BhZ2VzL0tpZHNbMyAwIFJdL0NvdW50IDE+PgplbmRvYmoKMyAwIG9iago8PC9UeXBlL1BhZ2UvTWVkaWFCb3hbMCAwIDYxMiA3OTJdL1BhcmVudCAyIDAgUi9SZXNvdXJjZXM8PC9Gb250PDwvRjEgNCAwIFI+Pj4+L0NvbnRlbnRzIDUgMCBSPj4KZW5kb2JqCjQgMCBvYmoKPDwvVHlwZS9Gb250L1N1YnR5cGUvVHlwZTEvQmFzZUZvbnQvSGVsdmV0aWNhPj4KZW5kb2JqCjUgMCBvYmoKPDwvTGVuZ3RoIDQ0Pj4Kc3RyZWFtCkJUCi9GMSA0OCBUZgoxMCA3MDAgVGQKKEhlbGxvIFdvcmxkKSBUagpFVAplbmRzdHJlYW0KZW5kb2JqCnhyZWYKMCA2CjAwMDAwMDAwMDAgNjU1MzUgZiAKMDAwMDAwMDAxNSAwMDAwMCBuIAowMDAwMDAwMDY0IDAwMDAwIG4gCjAwMDAwMDAxMjEgMDAwMDAgbiAKMDAwMDAwMDI2OCAwMDAwMCBuIAowMDAwMDAwMzM1IDAwMDAwIG4gCnRyYWlsZXIKPDwvU2l6ZSA2L1Jvb3QgMSAwIFI+PgpzdGFydHhyZWYKNDI3CiUlRU9G",
  "SUFFIX": ".pdf"
}
```

**cURL:**
```bash
curl -X POST "https://server.com/odata/Priority/tabula.ini/company/ORDERS('SO001')/EXTFILES_SUBFORM" \
  -u "apiuser:password" \
  -H "Content-Type: application/json" \
  -d '{
    "EXTFILEDES": "Order Contract",
    "EXTFILENAME": "data:application/pdf;base64,JVBERi0xLjQK...",
    "SUFFIX": ".pdf"
  }'
```

**המרת קובץ ל-Base64 (JavaScript):**
```javascript
async function uploadFile(orderNumber, file, description) {
  // קריאת הקובץ
  const arrayBuffer = await file.arrayBuffer();
  
  // המרה ל-Base64
  const base64 = btoa(
    String.fromCharCode(...new Uint8Array(arrayBuffer))
  );
  
  // יצירת Data URI
  const mimeType = file.type || 'application/octet-stream';
  const dataUri = `data:${mimeType};base64,${base64}`;
  
  // שליחה לשרת
  const response = await fetch(
    `https://server.com/odata/Priority/tabula.ini/company/ORDERS('${orderNumber}')/EXTFILES_SUBFORM`,
    {
      method: 'POST',
      headers: {
        'Authorization': 'Basic ' + btoa('apiuser:password'),
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        EXTFILEDES: description,
        EXTFILENAME: dataUri,
        SUFFIX: '.' + file.name.split('.').pop()
      })
    }
  );
  
  return await response.json();
}
```

### 6.5 הוספת/עדכון טקסט

**Request:**
```http
POST https://server.com/odata/Priority/tabula.ini/company/ORDERS('SO001')/ORDERSTEXT_SUBFORM
Authorization: Basic {credentials}
Content-Type: application/json

{
  "TEXT": "<p>Important customer notes:</p><ul><li>Rush delivery required</li><li>Special packaging needed</li></ul>",
  "APPEND": false,
  "SIGNATURE": false
}
```

**cURL:**
```bash
curl -X POST "https://server.com/odata/Priority/tabula.ini/company/ORDERS('SO001')/ORDERSTEXT_SUBFORM" \
  -u "apiuser:password" \
  -H "Content-Type: application/json" \
  -d '{
    "TEXT": "<p>Rush delivery required</p>",
    "APPEND": true,
    "SIGNATURE": false
  }'
```

**פרמטרים:**
- `TEXT` - תוכן HTML או טקסט רגיל
- `APPEND` - true = הוספה לטקסט קיים, false = החלפת הטקסט
- `SIGNATURE` - true = הוספת חתימת המשתמש

---

## 7. פעולות עדכון (Update Operations)

### 7.1 עדכון רשומה בסיסית

**Request:**
```http
PATCH https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS('C001')
Authorization: Basic {credentials}
Content-Type: application/json
Accept: application/json

{
  "EMAIL": "[email protected]",
  "PHONE": "+1-555-1111"
}
```

**cURL:**
```bash
curl -X PATCH "https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS('C001')" \
  -u "apiuser:password" \
  -H "Content-Type: application/json" \
  -d '{
    "EMAIL": "[email protected]",
    "PHONE": "+1-555-1111"
  }'
```

**Response:**
```json
{
  "@odata.context": "https://server.com/$metadata#CUSTOMERS/$entity",
  "CUSTNAME": "C001",
  "CUSTDES": "ACME Corporation",
  "EMAIL": "[email protected]",
  "PHONE": "+1-555-1111",
  "ADDRESS": "123 Main St"
}
```

**שים לב:** יש לעדכן לפי Unique Key, לא Auto-unique Key

### 7.2 עדכון רשומה בטבלת משנה

**Request:**
```http
PATCH https://server.com/odata/Priority/tabula.ini/company/ORDERS('SO001')/ORDERITEMS_SUBFORM(1)
Authorization: Basic {credentials}
Content-Type: application/json

{
  "TQUANT": 15,
  "PRICE": 95.00
}
```

**cURL:**
```bash
curl -X PATCH "https://server.com/odata/Priority/tabula.ini/company/ORDERS('SO001')/ORDERITEMS_SUBFORM(1)" \
  -u "apiuser:password" \
  -H "Content-Type: application/json" \
  -d '{
    "TQUANT": 15,
    "PRICE": 95.00
  }'
```

**Response:**
```json
{
  "@odata.context": "https://server.com/$metadata#ORDERS('SO001')/ORDERITEMS_SUBFORM/$entity",
  "KLINE": 1,
  "PARTNAME": "ITEM001",
  "PDES": "Product 1",
  "TQUANT": 15,
  "PRICE": 95.00,
  "TOTPRICE": 1425.00
}
```

---

## 8. פעולות מחיקה (Delete Operations)

### 8.1 מחיקת רשומה

**Request:**
```http
DELETE https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS('C999')
Authorization: Basic {credentials}
```

**cURL:**
```bash
curl -X DELETE "https://server.com/odata/Priority/tabula.ini/company/CUSTOMERS('C999')" \
  -u "apiuser:password"
```

**Response:** 204 No Content (הצלחה)

### 8.2 מחיקת רשומה בטבלת משנה

**Request:**
```http
DELETE https://server.com/odata/Priority/tabula.ini/company/ORDERS('SO001')/ORDERITEMS_SUBFORM(3)
Authorization: Basic {credentials}
```

**cURL:**
```bash
curl -X DELETE "https://server.com/odata/Priority/tabula.ini/company/ORDERS('SO001')/ORDERITEMS_SUBFORM(3)" \
  -u "apiuser:password"
```

**Response:** 204 No Content

---

## 9. פעולות אצווה (Batch Operations)

### 9.1 Batch בסיסי

**Request:**
```http
POST https://server.com/odata/Priority/tabula.ini/company/$batch
Authorization: Basic {credentials}
Content-Type: application/json
Accept: application/json

{
  "requests": [
    {
      "id": "1",
      "method": "POST",
      "url": "CUSTOMERS",
      "headers": {
        "content-type": "application/json"
      },
      "body": {
        "CUSTNAME": "C888",
        "CUSTDES": "Batch Customer"
      }
    },
    {
      "id": "2",
      "method": "GET",
      "url": "CUSTOMERS('C001')"
    },
    {
      "id": "3",
      "method": "PATCH",
      "url": "CUSTOMERS('C002')",
      "headers": {
        "content-type": "application/json"
      },
      "body": {
        "EMAIL": "[email protected]"
      }
    }
  ]
}
```

**cURL:**
```bash
curl -X POST "https://server.com/odata/Priority/tabula.ini/company/\$batch" \
  -u "apiuser:password" \
  -H "Content-Type: application/json" \
  -d '{
    "requests": [
      {
        "id": "1",
        "method": "GET",
        "url": "CUSTOMERS('\''C001'\'')"
      },
      {
        "id": "2",
        "method": "GET",
        "url": "CUSTOMERS('\''C002'\'')"
      }
    ]
  }'
```

### 9.2 Batch עם תלות בין בקשות

**Request:**
```http
POST https://server.com/odata/Priority/tabula.ini/company/$batch
Authorization: Basic {credentials}
Content-Type: application/json

{
  "requests": [
    {
      "id": "1",
      "method": "POST",
      "url": "ORDERS",
      "body": {
        "CUSTNAME": "C001"
      }
    },
    {
      "id": "2",
      "method": "POST",
      "url": "$1/ORDERITEMS_SUBFORM",
      "dependsOn": ["1"],
      "body": {
        "PARTNAME": "ITEM001",
        "TQUANT": 5,
        "DUEDATE": "2024-11-20T00:00:00+02:00"
      }
    },
    {
      "id": "3",
      "method": "POST",
      "url": "$1/ORDERITEMS_SUBFORM",
      "dependsOn": ["1"],
      "body": {
        "PARTNAME": "ITEM002",
        "TQUANT": 3,
        "DUEDATE": "2024-11-20T00:00:00+02:00"
      }
    }
  ]
}
```

**הסבר:**
- בקשה 1 יוצרת הזמנה חדשה
- `$1` מתייחס לתוצאה של בקשה 1
- `dependsOn: ["1"]` מבטיח שבקשות 2 ו-3 ירוצו רק אחרי הצלחת בקשה 1

### 9.3 טיפול בתשובות Batch

**JavaScript Example:**
```javascript
async function processBatchResponse(response) {
  const data = await response.json();
  
  for (const result of data.responses) {
    console.log(`Request ID: ${result.id}`);
    console.log(`Status: ${result.status}`);
    
    if (result.status >= 200 && result.status < 300) {
      console.log('Success:', result.body);
    } else {
      console.error('Error:', result.body);
    }
  }
}
```

---

## 10. Webhooks ואוטומציה

> **דרישה:** מודול Webhooks (רכישה נפרדת) + Priority v20.1+
>
> **תיעוד:** [Priority Webhooks Documentation](https://prioritysoftware.github.io/webhooks)

### 10.1 הגדרת Webhook ב-Priority UI

**מיקום:**
```
System Management > System Maintenance > Periodic Maintenance > 
BPM Maintenance > Webhook Definitions
```

**שדות נדרשים:**
- Webhook Name - שם זיהוי
- Target URL - כתובת יעד (HTTPS בלבד)
- Authentication Token - טוקן אבטחה

### 10.2 מבנה Webhook Payload

**Headers שנשלחים:**
```http
priority-form-name: ORDERS
priority-bpm-subject: Order Status Changed
priority-bpm-id: 245988
priority-bpm-name: Order Status Webhook
priority-bpm-token: D6D4545CE5AB4827BB51CA3C03005D1D
Content-Type: application/json
```

**Body (JSON):**
```json
{
  "ORDERS": {
    "ORDNAME": "SO24001234",
    "CUSTNAME": "C001",
    "CDES": "ACME Corporation",
    "ORDSTATUSDES": "Confirmed",
    "CURDATE": "2024-10-19T00:00:00+02:00",
    "TOTPRICE": 2500.00
  },
  "ORDERITEMS_SUBFORM": [
    {
      "KLINE": 1,
      "PARTNAME": "ITEM001",
      "TQUANT": 5,
      "PRICE": 100.00
    }
  ]
}
```

### 10.3 קבלת Webhook (Endpoint Example)

**Node.js/Express:**
```javascript
const express = require('express');
const app = express();

app.use(express.json());

app.post('/webhooks/priority/orders', (req, res) => {
  // בדיקת טוקן אבטחה
  const token = req.headers['priority-bpm-token'];
  const expectedToken = 'D6D4545CE5AB4827BB51CA3C03005D1D';
  
  if (token !== expectedToken) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  
  // קבלת הנתונים
  const formName = req.headers['priority-form-name'];
  const bpmSubject = req.headers['priority-bpm-subject'];
  const data = req.body;
  
  console.log(`Webhook received: ${bpmSubject}`);
  console.log(`Form: ${formName}`);
  console.log('Data:', JSON.stringify(data, null, 2));
  
  // עיבוד הנתונים
  if (data.ORDERS) {
    const order = data.ORDERS;
    console.log(`Order ${order.ORDNAME} status: ${order.ORDSTATUSDES}`);
    
    // שלח התראה, עדכן מערכת חיצונית וכו'
    sendNotification(order);
    updateExternalSystem(order);
  }
  
  // החזרת תשובה מוצלחת
  res.status(200).json({ received: true });
});

app.listen(3000, () => {
  console.log('Webhook receiver listening on port 3000');
});
```

**Python/Flask:**
```python
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/webhooks/priority/orders', methods=['POST'])
def priority_webhook():
    # בדיקת טוקן
    token = request.headers.get('priority-bpm-token')
    expected_token = 'D6D4545CE5AB4827BB51CA3C03005D1D'
    
    if token != expected_token:
        return jsonify({'error': 'Unauthorized'}), 401
    
    # קבלת נתונים
    form_name = request.headers.get('priority-form-name')
    bpm_subject = request.headers.get('priority-bpm-subject')
    data = request.json
    
    print(f'Webhook received: {bpm_subject}')
    print(f'Form: {form_name}')
    print(f'Data: {data}')
    
    # עיבוד
    if 'ORDERS' in data:
        order = data['ORDERS']
        print(f"Order {order['ORDNAME']} status: {order['ORDSTATUSDES']}")
        
        # טיפול בהזמנה
        process_order(order)
    
    return jsonify({'received': True}), 200

if __name__ == '__main__':
    app.run(port=3000)
```

---

## 11. דוגמאות מעשיות מלאות

### 11.1 סנכרון לקוחות (Incremental Sync)

```javascript
// Incremental Customer Sync with Error Handling
class PriorityCustomerSync {
  constructor(baseUrl, username, password) {
    this.baseUrl = baseUrl;
    this.credentials = Buffer.from(`${username}:${password}`).toString('base64');
    this.lastSyncFile = 'last_sync_date.txt';
  }
  
  async syncCustomers() {
    try {
      // קריאת תאריך סנכרון אחרון
      const lastSync = this.getLastSyncDate();
      const lastSyncEncoded = lastSync.toISOString().replace('+', '%2B');
      
      console.log(`Starting sync from: ${lastSync.toISOString()}`);
      
      // בניית URL
      const url = `${this.baseUrl}/CUSTOMERS?` +
                  `$since=${lastSyncEncoded}` +
                  `&$select=CUSTNAME,CUSTDES,EMAIL,PHONE,ADDRESS,STATENAME` +
                  `&$expand=CUSTNOTESA_SUBFORM($select=NOTE,CURDATE)` +
                  `&$orderby=CUSTNAME`;
      
      // שליחת בקשה
      const response = await fetch(url, {
        headers: {
          'Authorization': `Basic ${this.credentials}`,
          'Accept': 'application/json'
        }
      });
      
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${await response.text()}`);
      }
      
      const data = await response.json();
      console.log(`Found ${data.value.length} changed customers`);
      
      // עיבוד לקוחות
      for (const customer of data.value) {
        await this.processCustomer(customer);
      }
      
      // שמירת תאריך סנכרון חדש
      this.saveLastSyncDate(new Date());
      
      console.log('Sync completed successfully');
      return { success: true, count: data.value.length };
      
    } catch (error) {
      console.error('Sync failed:', error);
      return { success: false, error: error.message };
    }
  }
  
  async processCustomer(customer) {
    console.log(`Processing customer: ${customer.CUSTNAME} - ${customer.CUSTDES}`);
    
    // עדכון מסד נתונים מקומי
    await this.updateLocalDatabase(customer);
    
    // שליחת התראה אם נוסף לקוח חדש
    if (customer.notes && customer.notes.includes('NEW')) {
      await this.sendNotification(`New customer: ${customer.CUSTDES}`);
    }
  }
  
  getLastSyncDate() {
    try {
      const fs = require('fs');
      const dateStr = fs.readFileSync(this.lastSyncFile, 'utf8');
      return new Date(dateStr);
    } catch {
      // אם אין קובץ, החזר תאריך ישן
      return new Date('2024-01-01');
    }
  }
  
  saveLastSyncDate(date) {
    const fs = require('fs');
    fs.writeFileSync(this.lastSyncFile, date.toISOString());
  }
  
  async updateLocalDatabase(customer) {
    // כאן תהיה הלוגיקה לעדכון DB מקומי
    console.log(`Updating DB for customer ${customer.CUSTNAME}`);
  }
  
  async sendNotification(message) {
    console.log(`Notification: ${message}`);
    // כאן תהיה לוגיקה לשליחת התראה
  }
}

// שימוש
const sync = new PriorityCustomerSync(
  'https://server.com/odata/Priority/tabula.ini/company',
  'apiuser',
  'password'
);

// הרצה מתוזמנת כל 15 דקות
setInterval(() => {
  sync.syncCustomers();
}, 15 * 60 * 1000);
```

### 11.2 יצירת הזמנה מלאה עם פריטים

```javascript
// Complete Order Creation System
class OrderManager {
  constructor(baseUrl, username, password) {
    this.baseUrl = baseUrl;
    this.credentials = Buffer.from(`${username}:${password}`).toString('base64');
  }
  
  async createOrderWithItems(customerCode, items, notes = '') {
    try {
      // שלב 1: יצירת הזמנה עם פריטים בבקשה אחת
      const orderData = {
        CUSTNAME: customerCode,
        ORDERITEMS_SUBFORM: items.map(item => ({
          PARTNAME: item.partNumber,
          TQUANT: item.quantity,
          DUEDATE: item.dueDate || this.getDefaultDueDate()
        }))
      };
      
      console.log('Creating order:', JSON.stringify(orderData, null, 2));
      
      const response = await fetch(`${this.baseUrl}/ORDERS`, {
        method: 'POST',
        headers: {
          'Authorization': `Basic ${this.credentials}`,
          'Content-Type': 'application/json',
          'Accept': 'application/json',
          'OData-Version': '4.0'
        },
        body: JSON.stringify(orderData)
      });
      
      if (!response.ok) {
        const error = await response.text();
        throw new Error(`Failed to create order: ${error}`);
      }
      
      const order = await response.json();
      console.log(`Order created: ${order.ORDNAME}`);
      
      // שלב 2: הוספת הערות אם יש
      if (notes) {
        await this.addOrderNotes(order.ORDNAME, notes);
      }
      
      // שלב 3: קבלת הנתונים המלאים
      const fullOrder = await this.getFullOrderDetails(order.ORDNAME);
      
      return {
        success: true,
        orderNumber: order.ORDNAME,
        order: fullOrder
      };
      
    } catch (error) {
      console.error('Order creation failed:', error);
      return {
        success: false,
        error: error.message
      };
    }
  }
  
  async addOrderNotes(orderNumber, notes) {
    const htmlNotes = `<p>${notes}</p><p><i>Created via API on ${new Date().toLocaleString()}</i></p>`;
    
    const response = await fetch(
      `${this.baseUrl}/ORDERS('${orderNumber}')/ORDERSTEXT_SUBFORM`,
      {
        method: 'POST',
        headers: {
          'Authorization': `Basic ${this.credentials}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          TEXT: htmlNotes,
          APPEND: false,
          SIGNATURE: false
        })
      }
    );
    
    if (!response.ok) {
      console.warn('Failed to add notes:', await response.text());
    }
  }
  
  async getFullOrderDetails(orderNumber) {
    const url = `${this.baseUrl}/ORDERS('${orderNumber}')?` +
                `$select=ORDNAME,CUSTNAME,CDES,CURDATE,ORDSTATUSDES,TOTPRICE` +
                `&$expand=ORDERITEMS_SUBFORM($select=KLINE,PARTNAME,PDES,TQUANT,PRICE,TOTPRICE)`;
    
    const response = await fetch(url, {
      headers: {
        'Authorization': `Basic ${this.credentials}`,
        'Accept': 'application/json'
      }
    });
    
    return await response.json();
  }
  
  getDefaultDueDate() {
    const date = new Date();
    date.setDate(date.getDate() + 14); // 2 שבועות מהיום
    return date.toISOString().split('T')[0] + 'T00:00:00+02:00';
  }
}

// שימוש
const orderManager = new OrderManager(
  'https://server.com/odata/Priority/tabula.ini/company',
  'apiuser',
  'password'
);

const items = [
  { partNumber: 'ITEM001', quantity: 10 },
  { partNumber: 'ITEM002', quantity: 5 },
  { partNumber: 'ITEM003', quantity: 3 }
];

const result = await orderManager.createOrderWithItems(
  'C001',
  items,
  'Rush order - deliver by end of week'
);

if (result.success) {
  console.log(`Order ${result.orderNumber} created successfully`);
  console.log('Order details:', result.order);
} else {
  console.error('Failed:', result.error);
}
```

### 11.3 בדיקת מלאי עם התראות

```javascript
// Inventory Check and Alert System
class InventoryMonitor {
  constructor(baseUrl, username, password, alertThreshold = 10) {
    this.baseUrl = baseUrl;
    this.credentials = Buffer.from(`${username}:${password}`).toString('base64');
    this.alertThreshold = alertThreshold;
  }
  
  async checkLowStock() {
    try {
      const url = `${this.baseUrl}/LOGPART?` +
                  `$filter=ONHAND lt ${this.alertThreshold} and TYPE eq 'P'` +
                  `&$select=PARTNAME,PARTDES,ONHAND,MINBAL,LASTPRICE,FAMILYNAME` +
                  `&$orderby=ONHAND asc` +
                  `&$top=100`;
      
      const response = await fetch(url, {
        headers: {
          'Authorization': `Basic ${this.credentials}`,
          'Accept': 'application/json'
        }
      });
      
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }
      
      const data = await response.json();
      const lowStockItems = data.value;
      
      console.log(`Found ${lowStockItems.length} low stock items`);
      
      // סיווג לפי דחיפות
      const critical = lowStockItems.filter(item => item.ONHAND < item.MINBAL);
      const warning = lowStockItems.filter(item => 
        item.ONHAND >= item.MINBAL && item.ONHAND < this.alertThreshold
      );
      
      // שליחת התראות
      if (critical.length > 0) {
        await this.sendCriticalAlert(critical);
      }
      
      if (warning.length > 0) {
        await this.sendWarningAlert(warning);
      }
      
      // יצירת דוח
      return this.generateReport(lowStockItems, critical, warning);
      
    } catch (error) {
      console.error('Inventory check failed:', error);
      return null;
    }
  }
  
  async sendCriticalAlert(items) {
    console.log('\n🚨 CRITICAL - Below Minimum Balance:');
    items.forEach(item => {
      console.log(`  - ${item.PARTNAME}: ${item.ONHAND} units (min: ${item.MINBAL})`);
    });
    
    // כאן תהיה לוגיקה לשליחת אימייל/SMS
    const emailBody = this.formatAlertEmail(items, 'CRITICAL');
    // await sendEmail('[email protected]', 'Critical Inventory Alert', emailBody);
  }
  
  async sendWarningAlert(items) {
    console.log('\n⚠️ WARNING - Low Stock:');
    items.forEach(item => {
      console.log(`  - ${item.PARTNAME}: ${item.ONHAND} units`);
    });
  }
  
  formatAlertEmail(items, severity) {
    let html = `<h2>${severity} Inventory Alert</h2>`;
    html += '<table border="1"><tr><th>Part</th><th>Description</th><th>On Hand</th><th>Min Balance</th><th>Price</th></tr>';
    
    items.forEach(item => {
      html += `<tr>
        <td>${item.PARTNAME}</td>
        <td>${item.PARTDES}</td>
        <td style="color: red">${item.ONHAND}</td>
        <td>${item.MINBAL}</td>
        <td>$${item.LASTPRICE.toFixed(2)}</td>
      </tr>`;
    });
    
    html += '</table>';
    return html;
  }
  
  generateReport(all, critical, warning) {
    return {
      timestamp: new Date().toISOString(),
      summary: {
        total: all.length,
        critical: critical.length,
        warning: warning.length
      },
      items: all,
      criticalItems: critical,
      warningItems: warning
    };
  }
}

// שימוש - בדיקה כל שעה
const monitor = new InventoryMonitor(
  'https://server.com/odata/Priority/tabula.ini/company',
  'apiuser',
  'password',
  15 // התראה מתחת ל-15 יחידות
);

// בדיקה מיידית
const report = await monitor.checkLowStock();
console.log('\nInventory Report:', JSON.stringify(report.summary, null, 2));

// תזמון לבדיקה כל שעה
setInterval(() => {
  monitor.checkLowStock();
}, 60 * 60 * 1000);
```

### 11.4 דוח מכירות יומי

```javascript
// Daily Sales Report Generator
class SalesReporter {
  constructor(baseUrl, username, password) {
    this.baseUrl = baseUrl;
    this.credentials = Buffer.from(`${username}:${password}`).toString('base64');
  }
  
  async generateDailySalesReport(date = new Date()) {
    try {
      // הגדרת טווח תאריכים
      const startOfDay = new Date(date);
      startOfDay.setHours(0, 0, 0, 0);
      
      const endOfDay = new Date(date);
      endOfDay.setHours(23, 59, 59, 999);
      
      const startISO = startOfDay.toISOString().replace('+', '%2B');
      const endISO = endOfDay.toISOString().replace('+', '%2B');
      
      // שליפת הזמנות
      const url = `${this.baseUrl}/ORDERS?` +
                  `$filter=CURDATE ge ${startISO} and CURDATE le ${endISO}` +
                  `&$select=ORDNAME,CUSTNAME,CDES,CURDATE,TOTPRICE,ORDSTATUSDES` +
                  `&$expand=ORDERITEMS_SUBFORM($select=PARTNAME,PDES,TQUANT,PRICE,TOTPRICE)` +
                  `&$orderby=CURDATE desc`;
      
      const response = await fetch(url, {
        headers: {
          'Authorization': `Basic ${this.credentials}`,
          'Accept': 'application/json'
        }
      });
      
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}`);
      }
      
      const data = await response.json();
      const orders = data.value;
      
      // חישוב סטטיסטיקות
      const stats = this.calculateStatistics(orders);
      
      // יצירת דוח
      const report = this.formatReport(date, orders, stats);
      
      // שמירה לקובץ
      this.saveReport(report, date);
      
      // שליחת דוח במייל
      // await this.emailReport(report);
      
      return report;
      
    } catch (error) {
      console.error('Report generation failed:', error);
      return null;
    }
  }
  
  calculateStatistics(orders) {
    const stats = {
      totalOrders: orders.length,
      totalRevenue: 0,
      averageOrderValue: 0,
      byStatus: {},
      byCustomer: {},
      topProducts: {}
    };
    
    orders.forEach(order => {
      // סה"כ הכנסות
      stats.totalRevenue += order.TOTPRICE || 0;
      
      // לפי סטטוס
      const status = order.ORDSTATUSDES || 'Unknown';
      stats.byStatus[status] = (stats.byStatus[status] || 0) + 1;
      
      // לפי לקוח
      const customer = order.CDES || 'Unknown';
      if (!stats.byCustomer[customer]) {
        stats.byCustomer[customer] = { count: 0, revenue: 0 };
      }
      stats.byCustomer[customer].count++;
      stats.byCustomer[customer].revenue += order.TOTPRICE || 0;
      
      // מוצרים פופולריים
      if (order.ORDERITEMS_SUBFORM) {
        order.ORDERITEMS_SUBFORM.forEach(item => {
          const product = item.PARTNAME;
          if (!stats.topProducts[product]) {
            stats.topProducts[product] = {
              description: item.PDES,
              quantity: 0,
              revenue: 0
            };
          }
          stats.topProducts[product].quantity += item.TQUANT;
          stats.topProducts[product].revenue += item.TOTPRICE || 0;
        });
      }
    });
    
    stats.averageOrderValue = stats.totalOrders > 0 
      ? stats.totalRevenue / stats.totalOrders 
      : 0;
    
    return stats;
  }
  
  formatReport(date, orders, stats) {
    const dateStr = date.toISOString().split('T')[0];
    
    let report = `
╔═══════════════════════════════════════════
   DAILY SALES REPORT - ${dateStr}
╚═══════════════════════════════════════════

SUMMARY
───────────────────────────────────────────
Total Orders:        ${stats.totalOrders}
Total Revenue:       $${stats.totalRevenue.toFixed(2)}
Average Order Value: $${stats.averageOrderValue.toFixed(2)}

ORDERS BY STATUS
───────────────────────────────────────────
`;
    
    Object.entries(stats.byStatus).forEach(([status, count]) => {
      report += `${status.padEnd(20)} ${count}\n`;
    });
    
    report += `
TOP 10 CUSTOMERS
───────────────────────────────────────────
`;
    
    const topCustomers = Object.entries(stats.byCustomer)
      .sort((a, b) => b[1].revenue - a[1].revenue)
      .slice(0, 10);
    
    topCustomers.forEach(([customer, data]) => {
      report += `${customer.padEnd(30)} $${data.revenue.toFixed(2)}\n`;
    });
    
    report += `
TOP 10 PRODUCTS
───────────────────────────────────────────
`;
    
    const topProducts = Object.entries(stats.topProducts)
      .sort((a, b) => b[1].revenue - a[1].revenue)
      .slice(0, 10);
    
    topProducts.forEach(([product, data]) => {
      report += `${product.padEnd(15)} Qty: ${data.quantity.toString().padEnd(5)} Revenue: $${data.revenue.toFixed(2)}\n`;
    });
    
    report += `
╔═══════════════════════════════════════════
Generated: ${new Date().toISOString()}
╚═══════════════════════════════════════════
`;
    
    return report;
  }
  
  saveReport(report, date) {
    const fs = require('fs');
    const filename = `sales_report_${date.toISOString().split('T')[0]}.txt`;
    fs.writeFileSync(filename, report);
    console.log(`Report saved to: ${filename}`);
  }
}

// שימוש
const reporter = new SalesReporter(
  'https://server.com/odata/Priority/tabula.ini/company',
  'apiuser',
  'password'
);

// דוח להיום
const report = await reporter.generateDailySalesReport();
console.log(report);

// דוח לתאריך ספציפי
const yesterdayReport = await reporter.generateDailySalesReport(
  new Date('2024-10-18')
);
```

---

## נספחים

### נ1. קודי שגיאה נפוצים

| קוד | משמעות | פתרון |
|-----|--------|-------|
| 400 | Bad Request | בדוק syntax של URL ושדות בבקשה |
| 401 | Unauthorized | בדוק אימות (username/password/token) |
| 403 | Forbidden | בדוק הרשאות המשתמש |
| 404 | Not Found | בדוק שם Entity או ID |
| 500 | Server Error | שגיאה במערכת Priority - בדוק logs |

### נ2. טיפים לביצועים

**1. תמיד השתמש ב-$select:**
```
❌ GET /CUSTOMERS('C001')
✅ GET /CUSTOMERS('C001')?$select=CUSTNAME,CUSTDES,EMAIL
```

**2. הגבל תוצאות עם $top:**
```
❌ GET /ORDERS
✅ GET /ORDERS?$top=100
```

**3. השתמש ב-$since לסנכרון:**
```
❌ GET /ORDERS (מחזיר הכל)
✅ GET /ORDERS?$since=2024-10-01T00:00:00Z
```

**4. Batch במקום בקשות מרובות:**
```
❌ 10 POST requests
✅ 1 $batch request עם 10 פעולות
```

**5. Cache metadata:**
```javascript
// ❌ מבקש metadata בכל פעם
const metadata = await getMetadata();

// ✅ Cache במנגנון
if (!metadataCache.has(entity)) {
  metadataCache.set(entity, await getMetadata(entity));
}
```

### נ3. קישורים למשאבים

**תיעוד רשמי:**
- [Priority Developer Portal](https://prioritysoftware.github.io/)
- [REST API Guide](https://prioritysoftware.github.io/restapi/)
- [Authentication](https://prioritysoftware.github.io/restapi/authenticate/)
- [Querying Data](https://prioritysoftware.github.io/restapi/query/)
- [Modifying Data](https://prioritysoftware.github.io/restapi/modify/)
- [Webhooks](https://prioritysoftware.github.io/webhooks)
- [PDF Documentation](https://cdn.priority-software.com/docs/Priority_OData_API.pdf)

**OData:**
- [OData.org](http://www.odata.org/)
- [OData v4.0 Specification](http://docs.oasis-open.org/odata/odata/v4.0/)

**תמיכה:**
- [Priority Support](https://support.priority-software.com/)
- [Stack Overflow - priority-web-sdk](https://stackoverflow.com/questions/tagged/priority-web-sdk)
- [GitHub](https://github.com/PrioritySoftware)

**כלים:**
- [Postman](https://www.postman.com/) - לבדיקת API
- [cURL](https://curl.se/) - שורת פקודה
- [OData Explorer](http://www.odata.org/ecosystem/tools/)

---

**עודכן לאחרונה:** אוקטובר 2024  
**גרסה:** 2.0  
**מחבר:** מבוסס על התיעוד הרשמי של Priority Software