Si los campos ID de las dos tablas usas nombre diferente se tienen que especificar, en lo personal me es mas fácil usar esta estructura de nombres de campos ID y de campos relacionados, pero ya es cuestión de gustos.
Ya para actualizar seria algo así, sencillito y carismático como dicen los Argentinos jejej
El siguiente código funciona de forma general, es decir tanto para un registro nuevo o si estas editando un registro ya guardado, si cambiaste solo algo en la tabla encabezado, o solo modificaste algo de la tabla detalle, por eso es super importante usar campos ID autoincrementables en todas las tablas que vayan a administrar con FoxyDb te facilita el acceso a datos enormemente, y para prueba un botón jejej
LOCAL _RegistroNuevo, _UltimoId
* Saber si es un registro Nuevo
_RegistroNuevo = .f.
* Ultimo ID insertado en Encabezado
_UltimoId = 0
* Revisar si hay Cambios en el formulario, todas los cursores abiertos con Query() y Use()
IF oDb.CHANGES()
* Revisar Cambios en Tabla Encabezado
IF oDb.CursorChanges("encabezado")
* Actualizar Cambios en el servidor, el .t. es para Obtener ultimo ID
IF oDb.UPDATE("clientes",.t.)
_UltimoId = oDb.id_last
IF _UltimoId <> 0
_RegistroNuevo = .t.
ENDIF
ELSE
* Error al Actualizar Encabezado
RETURN .f.
ENDIF
ENDIF
* Revisar en Tabla Detalles
IF oDb.CursorChanges("detalles")
* Verificar si es registro nuevo y reemplazar ID de encabezado en IdEncabezado
IF _RegistroNuevo
replace detalles.idEncabezado WITH _UltimoId IN detalles ALL
ENDIF
* Actualizar Cambios en el servidor, no es necesario obtener el ultimo ID
IF oDb.UPDATE("detalles")
* Listo cambios actualizados
ELSE
* Error al actualizar Detalles
RETURN .f.
ENDIF
ENDIF
* Confirmar cambios
IF oDb.SAVE()
WAIT WINDOWS "Registro Guardado"
RETURN .t.
ELSE
* Error al aplicar Commit
RETURN .f.
ENDIF
ELSE
* No se han realizado cambios
ENDIF